1-- 2-- _cmake.lua 3-- Define the CMake action(s). 4-- Copyright (c) 2015 Miodrag Milanovic 5-- Modifications and additions in 2017 by Maurizio Petrarota 6-- 7 8local cmake = premake.cmake 9local tree = premake.tree 10 11local includestr = 'include_directories(../%s)' 12local definestr = 'add_definitions(-D%s)' 13 14 15local function is_excluded(prj, cfg, file) 16 if table.icontains(prj.excludes, file) then 17 return true 18 end 19 20 if table.icontains(cfg.excludes, file) then 21 return true 22 end 23 24 return false 25end 26 27function cmake.excludedFiles(prj, cfg, src) 28 for _, v in ipairs(src) do 29 if (is_excluded(prj, cfg, v)) then 30 _p(1, 'list(REMOVE_ITEM source_list ../%s)', v) 31 end 32 33 end 34end 35 36function cmake.list(value) 37 if #value > 0 then 38 return " " .. table.concat(value, " ") 39 else 40 return "" 41 end 42end 43 44function cmake.files(prj) 45 local ret = {} 46 local tr = premake.project.buildsourcetree(prj) 47 tree.traverse(tr, { 48 onbranchenter = function(node, depth) 49 end, 50 onbranchexit = function(node, depth) 51 end, 52 onleaf = function(node, depth) 53 assert(node, "unexpected empty node") 54 if node.cfg then 55 table.insert(ret, node.cfg.name) 56 _p(1, '../%s', node.cfg.name) 57 end 58 end, 59 }, true, 1) 60 61 return ret 62end 63 64function cmake.header(prj) 65 _p('# %s project autogenerated by GENie', premake.action.current().shortname) 66 _p('cmake_minimum_required(VERSION 2.8.4)') 67 _p('') 68 _p('project(%s)', premake.esc(prj.name)) 69end 70 71function cmake.customtasks(prj) 72 local dirs = {} 73 local tasks = {} 74 for _, custombuildtask in ipairs(prj.custombuildtask or {}) do 75 for _, buildtask in ipairs(custombuildtask or {}) do 76 table.insert(tasks, buildtask) 77 local d = string.format("${CMAKE_CURRENT_SOURCE_DIR}/../%s", path.getdirectory(path.getrelative(prj.location, buildtask[2]))) 78 if not table.contains(dirs, d) then 79 table.insert(dirs, d) 80 _p('file(MAKE_DIRECTORY \"%s\")', d) 81 end 82 end 83 end 84 _p('') 85 86 for _, buildtask in ipairs(tasks) do 87 local deps = string.format("${CMAKE_CURRENT_SOURCE_DIR}/../%s ", path.getrelative(prj.location, buildtask[1])) 88 local outputs = string.format("${CMAKE_CURRENT_SOURCE_DIR}/../%s ", path.getrelative(prj.location, buildtask[2])) 89 local msg = "" 90 91 for _, depdata in ipairs(buildtask[3] or {}) do 92 deps = deps .. string.format("${CMAKE_CURRENT_SOURCE_DIR}/../%s ", path.getrelative(prj.location, depdata)) 93 end 94 95 _p('add_custom_command(') 96 _p(1, 'OUTPUT %s', outputs) 97 _p(1, 'DEPENDS %s', deps) 98 99 for _, cmdline in ipairs(buildtask[4] or {}) do 100 if (cmdline:sub(1, 1) ~= "@") then 101 local cmd = cmdline 102 local num = 1 103 for _, depdata in ipairs(buildtask[3] or {}) do 104 cmd = string.gsub(cmd, "%$%(" .. num .. "%)", string.format("${CMAKE_CURRENT_SOURCE_DIR}/../%s ", path.getrelative(prj.location, depdata))) 105 num = num + 1 106 end 107 108 cmd = string.gsub(cmd, "%$%(<%)", string.format("${CMAKE_CURRENT_SOURCE_DIR}/../%s ", path.getrelative(prj.location, buildtask[1]))) 109 cmd = string.gsub(cmd, "%$%(@%)", outputs) 110 111 _p(1, 'COMMAND %s', cmd) 112 else 113 msg = cmdline 114 end 115 end 116 _p(1, 'COMMENT \"%s\"', msg) 117 _p(')') 118 _p('') 119 end 120end 121 122function cmake.depRules(prj) 123 local maintable = {} 124 for _, dependency in ipairs(prj.dependency) do 125 for _, dep in ipairs(dependency) do 126 if path.issourcefile(dep[1]) then 127 local dep1 = premake.esc(path.getrelative(prj.location, dep[1])) 128 local dep2 = premake.esc(path.getrelative(prj.location, dep[2])) 129 if not maintable[dep1] then maintable[dep1] = {} end 130 table.insert(maintable[dep1], dep2) 131 end 132 end 133 end 134 135 for key, _ in pairs(maintable) do 136 local deplist = {} 137 local depsname = string.format('%s_deps', path.getname(key)) 138 139 for _, d2 in pairs(maintable[key]) do 140 table.insert(deplist, d2) 141 end 142 _p('set(') 143 _p(1, depsname) 144 for _, v in pairs(deplist) do 145 _p(1, '${CMAKE_CURRENT_SOURCE_DIR}/../%s', v) 146 end 147 _p(')') 148 _p('') 149 _p('set_source_files_properties(') 150 _p(1, '\"${CMAKE_CURRENT_SOURCE_DIR}/../%s\"', key) 151 _p(1, 'PROPERTIES OBJECT_DEPENDS \"${%s}\"', depsname) 152 _p(')') 153 _p('') 154 end 155end 156 157function cmake.commonRules(conf, str) 158 local Dupes = {} 159 local t2 = {} 160 for _, cfg in ipairs(conf) do 161 local cfgd = iif(str == includestr, cfg.includedirs, cfg.defines) 162 for _, v in ipairs(cfgd) do 163 if(t2[v] == #conf - 1) then 164 _p(str, v) 165 table.insert(Dupes, v) 166 end 167 if not t2[v] then 168 t2[v] = 1 169 else 170 t2[v] = t2[v] + 1 171 end 172 end 173 end 174 return Dupes 175end 176 177function cmake.cfgRules(cfg, dupes, str) 178 for _, v in ipairs(cfg) do 179 if (not table.icontains(dupes, v)) then 180 _p(1, str, v) 181 end 182 end 183end 184 185function cmake.removeCrosscompiler(platforms) 186 for i = #platforms, 1, -1 do 187 if premake.platforms[platforms[i]].iscrosscompiler then 188 table.remove(platforms, i) 189 end 190 end 191end 192 193function cmake.project(prj) 194 io.indent = " " 195 cmake.header(prj) 196 _p('set(') 197 _p('source_list') 198 local source_files = cmake.files(prj) 199 _p(')') 200 _p('') 201 202 local nativeplatform = iif(os.is64bit(), "x64", "x32") 203 local cc = premake.gettool(prj) 204 local platforms = premake.filterplatforms(prj.solution, cc.platforms, "Native") 205 206 cmake.removeCrosscompiler(platforms) 207 208 local configurations = {} 209 210 for _, platform in ipairs(platforms) do 211 for cfg in premake.eachconfig(prj, platform) do 212 -- TODO: Extend support for 32-bit targets on 64-bit hosts 213 if cfg.platform == nativeplatform then 214 table.insert(configurations, cfg) 215 end 216 end 217 end 218 219 local commonIncludes = cmake.commonRules(configurations, includestr) 220 local commonDefines = cmake.commonRules(configurations, definestr) 221 _p('') 222 223 for _, cfg in ipairs(configurations) do 224 _p('if(CMAKE_BUILD_TYPE MATCHES \"%s\")', cfg.name) 225 226 -- list excluded files 227 cmake.excludedFiles(prj, cfg, source_files) 228 229 -- add includes directories 230 cmake.cfgRules(cfg.includedirs, commonIncludes, includestr) 231 232 -- add build defines 233 cmake.cfgRules(cfg.defines, commonDefines, definestr) 234 235 -- set CXX flags 236 _p(1, 'set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} %s\")', cmake.list(table.join(cc.getcppflags(cfg), cc.getcflags(cfg), cc.getcxxflags(cfg), cfg.buildoptions, cfg.buildoptions_cpp))) 237 238 -- set C flags 239 _p(1, 'set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} %s\")', cmake.list(table.join(cc.getcppflags(cfg), cc.getcflags(cfg), cfg.buildoptions, cfg.buildoptions_c))) 240 241 _p('endif()') 242 _p('') 243 end 244 245 -- force CPP if needed 246 if (prj.options.ForceCPP) then 247 _p('set_source_files_properties(${source_list} PROPERTIES LANGUAGE CXX)') 248 end 249 250 -- add custom tasks 251 cmake.customtasks(prj) 252 253 -- per-dependency build rules 254 cmake.depRules(prj) 255 256 for _, cfg in ipairs(configurations) do 257 _p('if(CMAKE_BUILD_TYPE MATCHES \"%s\")', cfg.name) 258 259 if (prj.kind == 'StaticLib') then 260 _p(1, 'add_library(%s STATIC ${source_list})', premake.esc(cfg.buildtarget.basename)) 261 end 262 263 if (prj.kind == 'SharedLib') then 264 _p(1, 'add_library(%s SHARED ${source_list})', premake.esc(cfg.buildtarget.basename)) 265 end 266 if (prj.kind == 'ConsoleApp' or prj.kind == 'WindowedApp') then 267 _p(1, 'add_executable(%s ${source_list})', premake.esc(cfg.buildtarget.basename)) 268 _p(1, 'target_link_libraries(%s%s%s)', premake.esc(cfg.buildtarget.basename), cmake.list(premake.esc(premake.getlinks(cfg, "siblings", "basename"))), cmake.list(cc.getlinkflags(cfg))) 269 end 270 _p('endif()') 271 _p('') 272 end 273end