codelite_project.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. --
  2. -- Name: codelite/codelite_project.lua
  3. -- Purpose: Generate a CodeLite C/C++ project file.
  4. -- Author: Ryan Pusztai
  5. -- Modified by: Andrea Zanellato
  6. -- Manu Evans
  7. -- Tom van Dijck
  8. -- Created: 2013/05/06
  9. -- Copyright: (c) 2008-2016 Jason Perkins and the Premake project
  10. --
  11. local p = premake
  12. local tree = p.tree
  13. local project = p.project
  14. local config = p.config
  15. local codelite = p.modules.codelite
  16. codelite.project = {}
  17. local m = codelite.project
  18. function codelite.getLinks(cfg)
  19. -- System libraries are undecorated, add the required extension
  20. return config.getlinks(cfg, "system", "fullpath")
  21. end
  22. function codelite.getSiblingLinks(cfg)
  23. -- If we need sibling projects to be listed explicitly, add them on
  24. return config.getlinks(cfg, "siblings", "fullpath")
  25. end
  26. m.elements = {}
  27. m.ctools = {
  28. gcc = "gnu gcc",
  29. clang = "clang",
  30. msc = "Visual C++",
  31. }
  32. m.cxxtools = {
  33. gcc = "gnu g++",
  34. clang = "clang++",
  35. msc = "Visual C++",
  36. }
  37. function m.getcompilername(cfg)
  38. local tool = _OPTIONS.cc or cfg.toolset or p.CLANG
  39. local toolset = p.tools[tool]
  40. if not toolset then
  41. error("Invalid toolset '" + (_OPTIONS.cc or cfg.toolset) + "'")
  42. end
  43. if p.languages.isc(cfg.language) then
  44. return m.ctools[tool]
  45. elseif p.languages.iscpp(cfg.language) then
  46. return m.cxxtools[tool]
  47. end
  48. end
  49. function m.getcompiler(cfg)
  50. local toolset = p.tools[_OPTIONS.cc or cfg.toolset or p.CLANG]
  51. if not toolset then
  52. error("Invalid toolset '" + (_OPTIONS.cc or cfg.toolset) + "'")
  53. end
  54. return toolset
  55. end
  56. local function configuration_iscustombuild(cfg)
  57. return cfg and (cfg.kind == p.MAKEFILE) and (#cfg.buildcommands > 0)
  58. end
  59. local function configuration_isfilelist(cfg)
  60. return cfg and (cfg.buildaction == "None") and not configuration_iscustombuild(cfg)
  61. end
  62. local function configuration_needresoptions(cfg)
  63. return cfg and config.findfile(cfg, ".rc") and not configuration_iscustombuild(cfg)
  64. end
  65. m.internalTypeMap = {
  66. ConsoleApp = "Console",
  67. WindowedApp = "Console",
  68. Makefile = "",
  69. SharedLib = "Library",
  70. StaticLib = "Library"
  71. }
  72. function m.header(prj)
  73. _p('<?xml version="1.0" encoding="UTF-8"?>')
  74. local type = m.internalTypeMap[prj.kind] or ""
  75. _x('<CodeLite_Project Name="%s" InternalType="%s">', prj.name, type)
  76. end
  77. function m.plugins(prj)
  78. -- _p(1, '<Plugins>')
  79. -- <Plugin Name="CMakePlugin">
  80. -- <Plugin Name="qmake">
  81. -- _p(1, '</Plugins>')
  82. _p(1, '<Plugins/>')
  83. end
  84. function m.description(prj)
  85. _p(1, '<Description/>')
  86. -- TODO: ...
  87. end
  88. function m.files(prj)
  89. local tr = project.getsourcetree(prj)
  90. tree.traverse(tr, {
  91. -- folders are handled at the internal nodes
  92. onbranchenter = function(node, depth)
  93. _p(depth, '<VirtualDirectory Name="%s">', node.name)
  94. end,
  95. onbranchexit = function(node, depth)
  96. _p(depth, '</VirtualDirectory>')
  97. end,
  98. -- source files are handled at the leaves
  99. onleaf = function(node, depth)
  100. local excludesFromBuild = {}
  101. for cfg in project.eachconfig(prj) do
  102. local cfgname = codelite.cfgname(cfg)
  103. local fcfg = p.fileconfig.getconfig(node, cfg)
  104. if not fcfg or fcfg.flags.ExcludeFromBuild then
  105. table.insert(excludesFromBuild, cfgname)
  106. end
  107. end
  108. if #excludesFromBuild > 0 then
  109. _p(depth, '<File Name="%s" ExcludeProjConfig="%s" />', node.relpath, table.concat(excludesFromBuild, ';'))
  110. else
  111. _p(depth, '<File Name="%s"/>', node.relpath)
  112. end
  113. end,
  114. }, true)
  115. end
  116. function m.dependencies(prj)
  117. -- TODO: dependencies don't emit a line for each config if there aren't any...
  118. -- _p(1, '<Dependencies/>')
  119. local dependencies = project.getdependencies(prj)
  120. for cfg in project.eachconfig(prj) do
  121. cfgname = codelite.cfgname(cfg)
  122. if #dependencies > 0 then
  123. _p(1, '<Dependencies Name="%s">', cfgname)
  124. for _, dependency in ipairs(dependencies) do
  125. _p(2, '<Project Name="%s"/>', dependency.name)
  126. end
  127. _p(1, '</Dependencies>')
  128. else
  129. _p(1, '<Dependencies Name="%s"/>', cfgname)
  130. end
  131. end
  132. end
  133. function m.global_compiler(prj)
  134. _p(3, '<Compiler Options="" C_Options="" Assembler="">')
  135. _p(4, '<IncludePath Value="."/>')
  136. _p(3, '</Compiler>')
  137. end
  138. function m.global_linker(prj)
  139. _p(3, '<Linker Options="">')
  140. _p(4, '<LibraryPath Value="."/>')
  141. _p(3, '</Linker>')
  142. end
  143. function m.global_resourceCompiler(prj)
  144. _p(3, '<ResourceCompiler Options=""/>')
  145. end
  146. m.elements.globalSettings = function(prj)
  147. return {
  148. m.global_compiler,
  149. m.global_linker,
  150. m.global_resourceCompiler,
  151. }
  152. end
  153. function m.compiler(cfg)
  154. if configuration_iscustombuild(cfg) or configuration_isfilelist(cfg) then
  155. _p(3, '<Compiler Required="no"/>')
  156. return
  157. end
  158. local toolset = m.getcompiler(cfg)
  159. local sysincludedirs = toolset.getincludedirs(cfg, {}, cfg.sysincludedirs, cfg.frameworkdirs)
  160. local forceincludes = toolset.getforceincludes(cfg)
  161. local cxxflags = table.concat(table.join(sysincludedirs, toolset.getcxxflags(cfg), forceincludes, cfg.buildoptions), ";")
  162. local cflags = table.concat(table.join(sysincludedirs, toolset.getcflags(cfg), forceincludes, cfg.buildoptions), ";")
  163. local asmflags = ""
  164. local pch = p.tools.gcc.getpch(cfg)
  165. local usepch = "yes"
  166. if pch == nil then
  167. pch = "";
  168. usepch = "no"
  169. end
  170. _x(3, '<Compiler Options="%s" C_Options="%s" Assembler="%s" Required="yes" PreCompiledHeader="%s" PCHInCommandLine="%s" UseDifferentPCHFlags="no" PCHFlags="">', cxxflags, cflags, asmflags, pch, usepch)
  171. for _, includedir in ipairs(cfg.includedirs) do
  172. _x(4, '<IncludePath Value="%s"/>', project.getrelative(cfg.project, includedir))
  173. end
  174. for _, define in ipairs(cfg.defines) do
  175. _p(4, '<Preprocessor Value="%s"/>', p.esc(define):gsub(' ', '\\ '))
  176. end
  177. _p(3, '</Compiler>')
  178. end
  179. function m.linker(cfg)
  180. if configuration_iscustombuild(cfg) or configuration_isfilelist(cfg) then
  181. _p(3, '<Linker Required="no"/>')
  182. return
  183. end
  184. local toolset = m.getcompiler(cfg)
  185. local flags = table.join(toolset.getldflags(cfg), toolset.getincludedirs(cfg, {}, nil, cfg.frameworkdirs), toolset.getrunpathdirs(cfg, table.join(cfg.runpathdirs, config.getsiblingtargetdirs(cfg))), cfg.linkoptions, toolset.getlinks(cfg))
  186. _x(3, '<Linker Required="yes" Options="%s">', table.concat(flags, ";"))
  187. for _, libdir in ipairs(cfg.libdirs) do
  188. _p(4, '<LibraryPath Value="%s"/>', project.getrelative(cfg.project, libdir))
  189. end
  190. _p(3, '</Linker>')
  191. end
  192. function m.resourceCompiler(cfg)
  193. if not configuration_needresoptions(cfg) then
  194. _p(3, '<ResourceCompiler Options="" Required="no"/>')
  195. return
  196. end
  197. local toolset = m.getcompiler(cfg)
  198. local defines = table.implode(toolset.getdefines(table.join(cfg.defines, cfg.resdefines)), "", ";", "")
  199. local options = table.concat(cfg.resoptions, ";")
  200. _x(3, '<ResourceCompiler Options="%s%s" Required="yes">', defines, options)
  201. for _, includepath in ipairs(table.join(cfg.sysincludedirs, cfg.includedirs, cfg.resincludedirs)) do
  202. _x(4, '<IncludePath Value="%s"/>', project.getrelative(cfg.project, includepath))
  203. end
  204. _p(3, '</ResourceCompiler>')
  205. end
  206. function m.general(cfg)
  207. if configuration_isfilelist(cfg) then
  208. _p(3, '<General IntermediateDirectory="." WorkingDirectory="." PauseExecWhenProcTerminates="no"/>')
  209. return
  210. end
  211. local prj = cfg.project
  212. local isExe = prj.kind == "WindowedApp" or prj.kind == "ConsoleApp"
  213. local targetpath = project.getrelative(prj, cfg.buildtarget.directory)
  214. local objdir = project.getrelative(prj, cfg.objdir)
  215. local targetname = project.getrelative(prj, cfg.buildtarget.abspath)
  216. local workingdir = cfg.debugdir or prj.location
  217. local command = iif(isExe, path.getrelative(workingdir, cfg.buildtarget.abspath), "")
  218. local cmdargs = iif(isExe, table.concat(cfg.debugargs, " "), "") -- TODO: should this be debugargs instead?
  219. local useseparatedebugargs = "no"
  220. local debugargs = ""
  221. local workingdir = iif(isExe, project.getrelative(prj, cfg.debugdir), "")
  222. local pauseexec = iif(prj.kind == "ConsoleApp", "yes", "no")
  223. local isguiprogram = iif(prj.kind == "WindowedApp", "yes", "no")
  224. local isenabled = iif(cfg.flags.ExcludeFromBuild, "no", "yes")
  225. _x(3, '<General OutputFile="%s" IntermediateDirectory="%s" Command="%s" CommandArguments="%s" UseSeparateDebugArgs="%s" DebugArguments="%s" WorkingDirectory="%s" PauseExecWhenProcTerminates="%s" IsGUIProgram="%s" IsEnabled="%s"/>',
  226. targetname, objdir, command, cmdargs, useseparatedebugargs, debugargs, workingdir, pauseexec, isguiprogram, isenabled)
  227. end
  228. function m.environment(cfg)
  229. local envs = table.concat(cfg.debugenvs, "\n")
  230. _p(3, '<Environment EnvVarSetName="&lt;Use Defaults&gt;" DbgSetName="&lt;Use Defaults&gt;">')
  231. _p(4, '<![CDATA[%s]]>', envs)
  232. _p(3, '</Environment>')
  233. end
  234. function m.debugger(cfg)
  235. _p(3, '<Debugger IsRemote="%s" RemoteHostName="%s" RemoteHostPort="%s" DebuggerPath="" IsExtended="%s">', iif(cfg.debugremotehost, "yes", "no"), cfg.debugremotehost or "", iif(cfg.debugport, tostring(cfg.debugport), ""), iif(cfg.debugextendedprotocol, "yes", "no"))
  236. if #cfg.debugsearchpaths > 0 then
  237. p.escaper(codelite.escElementText)
  238. _p(4, '<DebuggerSearchPaths>%s</DebuggerSearchPaths>', table.concat(p.esc(project.getrelative(cfg.project, cfg.debugsearchpaths)), "\n"))
  239. p.escaper(codelite.esc)
  240. else
  241. _p(4, '<DebuggerSearchPaths/>')
  242. end
  243. if #cfg.debugconnectcommands > 0 then
  244. p.escaper(codelite.escElementText)
  245. _p(4, '<PostConnectCommands>%s</PostConnectCommands>', table.concat(p.esc(cfg.debugconnectcommands), "\n"))
  246. p.escaper(codelite.esc)
  247. else
  248. _p(4, '<PostConnectCommands/>')
  249. end
  250. if #cfg.debugstartupcommands > 0 then
  251. p.escaper(codelite.escElementText)
  252. _p(4, '<StartupCommands>%s</StartupCommands>', table.concat(p.esc(cfg.debugstartupcommands), "\n"))
  253. p.escaper(codelite.esc)
  254. else
  255. _p(4, '<StartupCommands/>')
  256. end
  257. _p(3, '</Debugger>')
  258. end
  259. function m.preBuild(cfg)
  260. if #cfg.prebuildcommands > 0 or cfg.prebuildmessage then
  261. _p(3, '<PreBuild>')
  262. p.escaper(codelite.escElementText)
  263. if cfg.prebuildmessage then
  264. local command = os.translateCommandsAndPaths("@{ECHO} " .. cfg.prebuildmessage, cfg.project.basedir, cfg.project.location)
  265. _x(4, '<Command Enabled="yes">%s</Command>', command)
  266. end
  267. local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)
  268. for _, command in ipairs(commands) do
  269. _x(4, '<Command Enabled="yes">%s</Command>', command)
  270. end
  271. p.escaper(codelite.esc)
  272. _p(3, '</PreBuild>')
  273. end
  274. end
  275. function m.postBuild(cfg)
  276. if #cfg.postbuildcommands > 0 or cfg.postbuildmessage then
  277. _p(3, '<PostBuild>')
  278. p.escaper(codelite.escElementText)
  279. if cfg.postbuildmessage then
  280. local command = os.translateCommandsAndPaths("@{ECHO} " .. cfg.postbuildmessage, cfg.project.basedir, cfg.project.location)
  281. _x(4, '<Command Enabled="yes">%s</Command>', command)
  282. end
  283. local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)
  284. for _, command in ipairs(commands) do
  285. _x(4, '<Command Enabled="yes">%s</Command>', command)
  286. end
  287. p.escaper(codelite.esc)
  288. _p(3, '</PostBuild>')
  289. end
  290. end
  291. function m.customBuild(cfg)
  292. if not configuration_iscustombuild(cfg) then
  293. _p(3, '<CustomBuild Enabled="no"/>')
  294. return
  295. end
  296. local build = table.implode(cfg.buildcommands,"","","")
  297. local clean = table.implode(cfg.cleancommands,"","","")
  298. local rebuild = table.implode(cfg.rebuildcommands,"","","")
  299. _p(3, '<CustomBuild Enabled="yes">')
  300. _x(4, '<BuildCommand>%s</BuildCommand>', build)
  301. _x(4, '<CleanCommand>%s</CleanCommand>', clean)
  302. _x(4, '<RebuildCommand>%s</RebuildCommand>', rebuild)
  303. _p(4, '<PreprocessFileCommand></PreprocessFileCommand>')
  304. _p(4, '<SingleFileCommand></SingleFileCommand>')
  305. _p(4, '<MakefileGenerationCommand></MakefileGenerationCommand>')
  306. _p(4, '<ThirdPartyToolName></ThirdPartyToolName>')
  307. _p(4, '<WorkingDirectory></WorkingDirectory>')
  308. _p(3, '</CustomBuild>')
  309. end
  310. function m.additionalRules(cfg)
  311. if configuration_iscustombuild(cfg) then
  312. _p(3, '<AdditionalRules/>')
  313. return
  314. end
  315. _p(3, '<AdditionalRules>')
  316. _p(4, '<CustomPostBuild/>')
  317. local dependencies = {}
  318. local makefilerules = {}
  319. local function addrule(dependencies, makefilerules, config, filename)
  320. if #config.buildcommands == 0 or #config.buildOutputs == 0 then
  321. return false
  322. end
  323. local inputs = table.implode(project.getrelative(cfg.project, config.buildInputs), "", "", " ")
  324. if filename ~= "" and inputs ~= "" then
  325. filename = filename .. " "
  326. end
  327. local outputs = project.getrelative(cfg.project, config.buildOutputs[1])
  328. local buildmessage = ""
  329. if config.buildmessage then
  330. buildmessage = "\t@{ECHO} " .. config.buildmessage .. "\n"
  331. end
  332. local commands = table.implode(config.buildCommands,"\t","\n","")
  333. table.insert(makefilerules, os.translateCommandsAndPaths(outputs .. ": " .. filename .. inputs .. "\n" .. buildmessage .. commands, cfg.project.basedir, cfg.project.location))
  334. table.insertflat(dependencies, outputs)
  335. return true
  336. end
  337. local tr = project.getsourcetree(cfg.project)
  338. p.tree.traverse(tr, {
  339. onleaf = function(node, depth)
  340. local filecfg = p.fileconfig.getconfig(node, cfg)
  341. local prj = cfg.project
  342. local rule = p.global.getRuleForFile(node.name, prj.rules)
  343. if not addrule(dependencies, makefilerules, filecfg, node.relpath) and rule then
  344. local environ = table.shallowcopy(filecfg.environ)
  345. if rule.propertydefinition then
  346. p.rule.prepareEnvironment(rule, environ, cfg)
  347. p.rule.prepareEnvironment(rule, environ, filecfg)
  348. end
  349. local rulecfg = p.context.extent(rule, environ)
  350. addrule(dependencies, makefilerules, rulecfg, node.relpath)
  351. end
  352. end
  353. })
  354. addrule(dependencies, makefilerules, cfg, "")
  355. if #makefilerules == 0 and #dependencies == 0 then
  356. _p(4, '<CustomPreBuild/>')
  357. else
  358. _p(4, '<CustomPreBuild>' .. table.implode(dependencies,"",""," "))
  359. _p(0, table.implode(makefilerules,"","","\n") .. '</CustomPreBuild>')
  360. end
  361. _p(3, '</AdditionalRules>')
  362. end
  363. function m.isCpp11(cfg)
  364. return (cfg.cppdialect == 'gnu++11') or (cfg.cppdialect == 'C++11') or (cfg.cppdialect == 'gnu++0x') or (cfg.cppdialect == 'C++0x')
  365. end
  366. function m.isCpp14(cfg)
  367. return (cfg.cppdialect == 'gnu++14') or (cfg.cppdialect == 'C++14') or (cfg.cppdialect == 'gnu++1y') or (cfg.cppdialect == 'C++1y')
  368. end
  369. function m.completion(cfg)
  370. _p(3, '<Completion EnableCpp11="%s" EnableCpp14="%s">',
  371. iif(m.isCpp11(cfg), "yes", "no"),
  372. iif(m.isCpp14(cfg), "yes", "no")
  373. )
  374. _p(4, '<ClangCmpFlagsC/>')
  375. _p(4, '<ClangCmpFlags/>')
  376. _p(4, '<ClangPP/>') -- TODO: we might want to set special code completion macros...?
  377. _p(4, '<SearchPaths/>') -- TODO: search paths for code completion?
  378. _p(3, '</Completion>')
  379. end
  380. m.elements.settings = function(cfg)
  381. return {
  382. m.compiler,
  383. m.linker,
  384. m.resourceCompiler,
  385. m.general,
  386. m.environment,
  387. m.debugger,
  388. m.preBuild,
  389. m.postBuild,
  390. m.customBuild,
  391. m.additionalRules,
  392. m.completion,
  393. }
  394. end
  395. m.types =
  396. {
  397. ConsoleApp = "Executable",
  398. Makefile = "",
  399. SharedLib = "Dynamic Library",
  400. StaticLib = "Static Library",
  401. WindowedApp = "Executable",
  402. Utility = "",
  403. }
  404. m.debuggers =
  405. {
  406. Default = "GNU gdb debugger",
  407. GDB = "GNU gdb debugger",
  408. LLDB = "LLDB Debugger",
  409. }
  410. function m.settings(prj)
  411. _p(1, '<Settings Type="%s">', m.types[prj.kind] or "")
  412. _p(2, '<GlobalSettings>')
  413. p.callArray(m.elements.globalSettings, prj)
  414. _p(2, '</GlobalSettings>')
  415. for cfg in project.eachconfig(prj) do
  416. local cfgname = codelite.cfgname(cfg)
  417. local compiler = m.getcompilername(cfg)
  418. local debugger = m.debuggers[cfg.debugger] or m.debuggers.Default
  419. local type = m.types[cfg.kind]
  420. _x(2, '<Configuration Name="%s" CompilerType="%s" DebuggerType="%s" Type="%s" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append">', cfgname, compiler, debugger, type)
  421. p.callArray(m.elements.settings, cfg)
  422. _p(2, '</Configuration>')
  423. end
  424. _p(1, '</Settings>')
  425. end
  426. m.elements.project = function(prj)
  427. return {
  428. m.header,
  429. m.plugins,
  430. m.description,
  431. m.files,
  432. m.dependencies,
  433. m.settings,
  434. }
  435. end
  436. --
  437. -- Project: Generate the CodeLite project file.
  438. --
  439. function m.generate(prj)
  440. p.utf8()
  441. p.callArray(m.elements.project, prj)
  442. _p('</CodeLite_Project>')
  443. end