From 84ad0f569e4b41aaa155d335753e1f4360c7d889 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 8 Aug 2021 11:11:04 +0200 Subject: [PATCH 01/27] - Handle `sysincludedirs` and `forceincludes`. - Remove extra quotes for `set_target_properties` which were problematic when buildoptions already got quotes. --- cmake_project.lua | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/cmake_project.lua b/cmake_project.lua index 146a429..6d8dddb 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -87,12 +87,28 @@ function m.generate(prj) _p('endif()') -- include dirs + + if #cfg.sysincludedirs > 0 then + _p('target_include_directories("%s" SYSTEM PRIVATE', prj.name) + for _, includedir in ipairs(cfg.sysincludedirs) do + _x(1, '$<$:%s>', cmake.cfgname(cfg), includedir) + end + _p(')') + end _p('target_include_directories("%s" PRIVATE', prj.name) for _, includedir in ipairs(cfg.includedirs) do _x(1, '$<$:%s>', cmake.cfgname(cfg), includedir) end _p(')') + if #cfg.forceincludes > 0 then + _p('if (MSVC)') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getforceincludes(cfg), "", "", " ")) + _p('else()') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getforceincludes(cfg), "", "", " ")) + _p('endif()') + end + -- defines _p('target_compile_definitions("%s" PRIVATE', prj.name) for _, define in ipairs(cfg.defines) do @@ -138,7 +154,7 @@ function m.generate(prj) if all_build_options ~= "" then _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) - _p(1, 'set_target_properties("%s" PROPERTIES COMPILE_FLAGS "%s")', prj.name, all_build_options) + _p(1, 'set_target_properties("%s" PROPERTIES COMPILE_FLAGS %s)', prj.name, all_build_options) _p('endif()') end From 45d7b4b513df81073024cc3eb151adca77050834 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 8 Aug 2021 11:36:31 +0200 Subject: [PATCH 02/27] Clean output: don't show empty "property". --- cmake_project.lua | 87 ++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 6d8dddb..d9d24d0 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -95,11 +95,13 @@ function m.generate(prj) end _p(')') end - _p('target_include_directories("%s" PRIVATE', prj.name) - for _, includedir in ipairs(cfg.includedirs) do - _x(1, '$<$:%s>', cmake.cfgname(cfg), includedir) + if #cfg.includedirs > 0 then + _p('target_include_directories("%s" PRIVATE', prj.name) + for _, includedir in ipairs(cfg.includedirs) do + _x(1, '$<$:%s>', cmake.cfgname(cfg), includedir) + end + _p(')') end - _p(')') if #cfg.forceincludes > 0 then _p('if (MSVC)') @@ -110,41 +112,47 @@ function m.generate(prj) end -- defines - _p('target_compile_definitions("%s" PRIVATE', prj.name) - for _, define in ipairs(cfg.defines) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), p.esc(define):gsub(' ', '\\ ')) + if #cfg.defines > 0 then + _p('target_compile_definitions("%s" PRIVATE', prj.name) + for _, define in ipairs(cfg.defines) do + _p(1, '$<$:%s>', cmake.cfgname(cfg), p.esc(define):gsub(' ', '\\ ')) + end + _p(')') end - _p(')') -- lib dirs - _p('target_link_directories("%s" PRIVATE', prj.name) - for _, libdir in ipairs(cfg.libdirs) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), libdir) + if #cfg.libdirs > 0 then + _p('target_link_directories("%s" PRIVATE', prj.name) + for _, libdir in ipairs(cfg.libdirs) do + _p(1, '$<$:%s>', cmake.cfgname(cfg), libdir) + end + _p(')') end - _p(')') -- libs - _p('target_link_libraries("%s"', prj.name) - -- Do not use toolset here as cmake needs to resolve dependency chains local uselinkgroups = isclangorgcc and cfg.linkgroups == p.ON - if uselinkgroups then - _p(1, '-Wl,--start-group') - end - for a, link in ipairs(config.getlinks(cfg, "dependencies", "object")) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), link.linktarget.basename) - end - if uselinkgroups then - -- System libraries don't depend on the project - _p(1, '-Wl,--end-group') - _p(1, '-Wl,--start-group') - end - for _, link in ipairs(config.getlinks(cfg, "system", "fullpath")) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), link) - end - if uselinkgroups then - _p(1, '-Wl,--end-group') + if uselinkgroups or # config.getlinks(cfg, "dependencies", "object") > 0 or #config.getlinks(cfg, "system", "fullpath") > 0 then + _p('target_link_libraries("%s"', prj.name) + -- Do not use toolset here as cmake needs to resolve dependency chains + if uselinkgroups then + _p(1, '-Wl,--start-group') + end + for a, link in ipairs(config.getlinks(cfg, "dependencies", "object")) do + _p(1, '$<$:%s>', cmake.cfgname(cfg), link.linktarget.basename) + end + if uselinkgroups then + -- System libraries don't depend on the project + _p(1, '-Wl,--end-group') + _p(1, '-Wl,--start-group') + end + for _, link in ipairs(config.getlinks(cfg, "system", "fullpath")) do + _p(1, '$<$:%s>', cmake.cfgname(cfg), link) + end + if uselinkgroups then + _p(1, '-Wl,--end-group') + end + _p(')') end - _p(')') -- setting build options all_build_options = "" @@ -170,16 +178,17 @@ function m.generate(prj) _p('endif()') end - - _p('target_compile_options("%s" PRIVATE', prj.name) + if #toolset.getcflags(cfg) > 0 or #toolset.getcxxflags(cfg) > 0 then + _p('target_compile_options("%s" PRIVATE', prj.name) - for _, flag in ipairs(toolset.getcflags(cfg)) do - _p(1, '$<$,$>:%s>', cmake.cfgname(cfg), flag) - end - for _, flag in ipairs(toolset.getcxxflags(cfg)) do - _p(1, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + for _, flag in ipairs(toolset.getcflags(cfg)) do + _p(1, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + end + for _, flag in ipairs(toolset.getcxxflags(cfg)) do + _p(1, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + end + _p(')') end - _p(')') -- C++ standard -- only need to configure it specified From c0bb61a9b9e974e7593c536ba927666277e41b3b Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Thu, 12 Aug 2021 20:46:56 +0200 Subject: [PATCH 03/27] Use project name instead of linktarget in `target_link_libraries`, so library path is correct. --- cmake_project.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake_project.lua b/cmake_project.lua index d9d24d0..e8c916e 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -138,7 +138,7 @@ function m.generate(prj) _p(1, '-Wl,--start-group') end for a, link in ipairs(config.getlinks(cfg, "dependencies", "object")) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), link.linktarget.basename) + _p(1, '$<$:%s>', cmake.cfgname(cfg), link.project.name) end if uselinkgroups then -- System libraries don't depend on the project From 334b5078923fadd9a002652b9b780914816ab32d Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Thu, 12 Aug 2021 21:36:04 +0200 Subject: [PATCH 04/27] Add support for pre/post buildcommands. --- cmake_project.lua | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index e8c916e..285576a 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -251,5 +251,36 @@ function m.generate(prj) _p('target_precompile_headers("%s" PUBLIC %s)', prj.name, pch) _p('endif()') end + + -- pre/post buildcommands + if cfg.prebuildmessage or #cfg.prebuildcommands > 0 then + -- add_custom_command PRE_BUILD runs just before generating the target + -- so instead, use add_custom_target to run it before any rule (as obj) + _p('add_custom_target(prebuild-%s', prj.name) + if cfg.prebuildmessage then + local command = os.translateCommandsAndPaths("{ECHO} " .. cfg.prebuildmessage, cfg.project.basedir, cfg.project.location) + _p(' COMMAND %s', command) + end + local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location) + for _, command in ipairs(commands) do + _p(' COMMAND %s', command) + end + _p(')') + _p('add_dependencies(%s prebuild-%s)', prj.name, prj.name) + end + + if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then + _p('add_custom_command(TARGET %s POST_BUILD', prj.name) + if cfg.postbuildmessage then + local command = os.translateCommandsAndPaths("{ECHO} " .. cfg.postbuildmessage, cfg.project.basedir, cfg.project.location) + _p(' COMMAND %s', command) + end + local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location) + for _, command in ipairs(commands) do + _p(' COMMAND %s', command) + end + _p(')') + end + end end From 4ce5a8af96b3d358ba8726347ebb3b1b2d873de6 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 15 Aug 2021 11:41:51 +0200 Subject: [PATCH 05/27] Add support for custom commands. --- cmake_project.lua | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index 285576a..9a6e474 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -50,6 +50,10 @@ function m.generate(prj) return end + local oldGetDefaultSeparator = path.getDefaultSeparator + path.getDefaultSeparator = function() return "/" end + + if prj.kind == 'StaticLib' then _p('add_library("%s" STATIC', prj.name) elseif prj.kind == 'SharedLib' then @@ -282,5 +286,38 @@ function m.generate(prj) _p(')') end + -- custom command + local function addCustomCommand(config, filename) + if #config.buildcommands == 0 or #config.buildOutputs == 0 then + return + end + _p('add_custom_command(TARGET OUTPUT %s', table.implode(config.buildOutputs,"",""," ")) + if config.buildmessage then + _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. config.buildmessage, config.project.basedir, config.project.location)) + end + for _, command in ipairs(config.buildCommands) do + _p(' COMMAND %s', os.translateCommandsAndPaths(command, config.project.basedir, config.project.location)) + end + if filename ~= "" and #config.buildInputs ~= 0 then + filename = filename .. " " + end + if filename ~= "" or #config.buildInputs ~= 0 then + _p(' DEPENDS %s', filename .. table.implode(config.buildInputs,"",""," ")) + end + _p(')') + + end + local tr = project.getsourcetree(cfg.project) + p.tree.traverse(tr, { + onleaf = function(node, depth) + local filecfg = p.fileconfig.getconfig(node, cfg) + if p.fileconfig.hasFileSettings(filecfg) then + addCustomCommand(filecfg, node.relpath) + end + end + }) + addCustomCommand(cfg, "") end +-- restore + path.getDefaultSeparator = oldGetDefaultSeparator end From 28032200d5b66c5d527e8644c48a30c6228c9346 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 7 Nov 2021 10:27:10 +0100 Subject: [PATCH 06/27] Add support for rules. --- cmake_project.lua | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 9a6e474..477aedc 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -36,6 +36,31 @@ function m.files(prj) tree.traverse(tr, { onleaf = function(node, depth) _p(depth, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) + + -- add generated files + for cfg in project.eachconfig(prj) do + local filecfg = p.fileconfig.getconfig(node, cfg) + local rule = p.global.getRuleForFile(node.name, prj.rules) + + if p.fileconfig.hasFileSettings(filecfg) then + for _, output in ipairs(filecfg.buildOutputs) do + _p(depth, '"%s"', path.getrelative(prj.workspace.location, output)) + end + break + elseif rule then + local environ = table.shallowcopy(filecfg.environ) + + if rule.propertydefinition then + p.rule.prepareEnvironment(rule, environ, cfg) + p.rule.prepareEnvironment(rule, environ, filecfg) + end + local rulecfg = p.context.extent(rule, environ) + for _, output in ipairs(rulecfg.buildOutputs) do + _p(depth, '"%s"', path.getrelative(prj.workspace.location, output)) + end + break + end + end end }, true) end @@ -287,32 +312,42 @@ function m.generate(prj) end -- custom command - local function addCustomCommand(config, filename) - if #config.buildcommands == 0 or #config.buildOutputs == 0 then + local function addCustomCommand(fileconfig, filename) + if #fileconfig.buildcommands == 0 or #fileconfig.buildOutputs == 0 then return end - _p('add_custom_command(TARGET OUTPUT %s', table.implode(config.buildOutputs,"",""," ")) - if config.buildmessage then - _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. config.buildmessage, config.project.basedir, config.project.location)) + _p('add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildOutputs),"",""," ")) + if fileconfig.buildmessage then + _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. fileconfig.buildmessage, cfg.project.basedir, cfg.project.location)) end - for _, command in ipairs(config.buildCommands) do - _p(' COMMAND %s', os.translateCommandsAndPaths(command, config.project.basedir, config.project.location)) + for _, command in ipairs(fileconfig.buildCommands) do + _p(' COMMAND %s', os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) end - if filename ~= "" and #config.buildInputs ~= 0 then + if filename ~= "" and #fileconfig.buildInputs ~= 0 then filename = filename .. " " end - if filename ~= "" or #config.buildInputs ~= 0 then - _p(' DEPENDS %s', filename .. table.implode(config.buildInputs,"",""," ")) + if filename ~= "" or #fileconfig.buildInputs ~= 0 then + _p(' DEPENDS %s', filename .. table.implode(fileconfig.buildInputs,"",""," ")) end _p(')') - end local tr = project.getsourcetree(cfg.project) p.tree.traverse(tr, { onleaf = function(node, depth) local filecfg = p.fileconfig.getconfig(node, cfg) + local rule = p.global.getRuleForFile(node.name, prj.rules) + if p.fileconfig.hasFileSettings(filecfg) then addCustomCommand(filecfg, node.relpath) + elseif rule then + local environ = table.shallowcopy(filecfg.environ) + + if rule.propertydefinition then + p.rule.prepareEnvironment(rule, environ, cfg) + p.rule.prepareEnvironment(rule, environ, filecfg) + end + local rulecfg = p.context.extent(rule, environ) + addCustomCommand(rulecfg, node.relpath) end end }) From c1c8ba54d85b64e0f31acc77b44d8627cec34879 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 7 Nov 2021 10:32:13 +0100 Subject: [PATCH 07/27] Replace some absolute paths by relative paths. --- cmake_project.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 477aedc..e9d5b5c 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -109,9 +109,9 @@ function m.generate(prj) -- output dir _p(1,'set_target_properties("%s" PROPERTIES', prj.name) _p(2, 'OUTPUT_NAME "%s"', cfg.buildtarget.basename) - _p(2, 'ARCHIVE_OUTPUT_DIRECTORY "%s"', cfg.buildtarget.directory) - _p(2, 'LIBRARY_OUTPUT_DIRECTORY "%s"', cfg.buildtarget.directory) - _p(2, 'RUNTIME_OUTPUT_DIRECTORY "%s"', cfg.buildtarget.directory) + _p(2, 'ARCHIVE_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) + _p(2, 'LIBRARY_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) + _p(2, 'RUNTIME_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) _p(1,')') _p('endif()') From 2d6f038c08f028de85bc3b21ed93079951dff6d2 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Thu, 14 Apr 2022 23:19:00 +0200 Subject: [PATCH 08/27] Handle project without source files. --- cmake_project.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake_project.lua b/cmake_project.lua index e9d5b5c..6a664e7 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -35,6 +35,7 @@ function m.files(prj) local tr = project.getsourcetree(prj) tree.traverse(tr, { onleaf = function(node, depth) + _p(depth, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) -- add generated files @@ -62,7 +63,7 @@ function m.files(prj) end end end - }, true) + }) end -- From b6347d5bb0d7876420a7ad037dd87242efd69207 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Thu, 14 Jul 2022 14:14:06 +0200 Subject: [PATCH 09/27] Fix case (might be problematic for override of premake module such as premake-qt) --- cmake_project.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 6a664e7..0bf1510 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -44,7 +44,7 @@ function m.files(prj) local rule = p.global.getRuleForFile(node.name, prj.rules) if p.fileconfig.hasFileSettings(filecfg) then - for _, output in ipairs(filecfg.buildOutputs) do + for _, output in ipairs(filecfg.buildoutputs) do _p(depth, '"%s"', path.getrelative(prj.workspace.location, output)) end break @@ -56,7 +56,7 @@ function m.files(prj) p.rule.prepareEnvironment(rule, environ, filecfg) end local rulecfg = p.context.extent(rule, environ) - for _, output in ipairs(rulecfg.buildOutputs) do + for _, output in ipairs(rulecfg.buildoutputs) do _p(depth, '"%s"', path.getrelative(prj.workspace.location, output)) end break @@ -314,21 +314,21 @@ function m.generate(prj) -- custom command local function addCustomCommand(fileconfig, filename) - if #fileconfig.buildcommands == 0 or #fileconfig.buildOutputs == 0 then + if #fileconfig.buildcommands == 0 or #fileconfig.buildoutputs == 0 then return end - _p('add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildOutputs),"",""," ")) + _p('add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) if fileconfig.buildmessage then _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. fileconfig.buildmessage, cfg.project.basedir, cfg.project.location)) end - for _, command in ipairs(fileconfig.buildCommands) do + for _, command in ipairs(fileconfig.buildcommands) do _p(' COMMAND %s', os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) end - if filename ~= "" and #fileconfig.buildInputs ~= 0 then + if filename ~= "" and #fileconfig.buildinputs ~= 0 then filename = filename .. " " end - if filename ~= "" or #fileconfig.buildInputs ~= 0 then - _p(' DEPENDS %s', filename .. table.implode(fileconfig.buildInputs,"",""," ")) + if filename ~= "" or #fileconfig.buildinputs ~= 0 then + _p(' DEPENDS %s', filename .. table.implode(fileconfig.buildinputs,"",""," ")) end _p(')') end From be33000b60ae6363b8e455d9bc296db873571046 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 17 Jul 2022 12:54:52 +0200 Subject: [PATCH 10/27] Fix single/double quote in *`buildmessage`. --- cmake_project.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 0bf1510..c7c5c25 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -288,7 +288,7 @@ function m.generate(prj) -- so instead, use add_custom_target to run it before any rule (as obj) _p('add_custom_target(prebuild-%s', prj.name) if cfg.prebuildmessage then - local command = os.translateCommandsAndPaths("{ECHO} " .. cfg.prebuildmessage, cfg.project.basedir, cfg.project.location) + local command = os.translateCommandsAndPaths("{ECHO} " .. premake.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location) _p(' COMMAND %s', command) end local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location) @@ -302,7 +302,7 @@ function m.generate(prj) if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then _p('add_custom_command(TARGET %s POST_BUILD', prj.name) if cfg.postbuildmessage then - local command = os.translateCommandsAndPaths("{ECHO} " .. cfg.postbuildmessage, cfg.project.basedir, cfg.project.location) + local command = os.translateCommandsAndPaths("{ECHO} " .. premake.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location) _p(' COMMAND %s', command) end local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location) @@ -319,7 +319,7 @@ function m.generate(prj) end _p('add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) if fileconfig.buildmessage then - _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. fileconfig.buildmessage, cfg.project.basedir, cfg.project.location)) + _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. premake.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) end for _, command in ipairs(fileconfig.buildcommands) do _p(' COMMAND %s', os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) From df8ca36d464a9a0c46e5e99b633f0edc208fdfa9 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 25 Sep 2022 14:07:54 +0200 Subject: [PATCH 11/27] Handle "missing" directories for custom command. --- cmake_project.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index c7c5c25..0802165 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -317,6 +317,11 @@ function m.generate(prj) if #fileconfig.buildcommands == 0 or #fileconfig.buildoutputs == 0 then return end + + local custom_output_directories = table.unique(table.translate(fileconfig.buildoutputs, function(output) return project.getrelative(cfg.project, path.getdirectory(output)) end)) + -- Alternative would be to add 'COMMAND ${CMAKE_COMMAND} -E make_directory %s' to below add_custom_command + _p('file(MAKE_DIRECTORY %s)', table.implode(custom_output_directories, "", "", " ")) + _p('add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) if fileconfig.buildmessage then _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. premake.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) From 00afba5c27fbd8a46bd0dc971c9a251d797d605c Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 2 Oct 2022 19:23:24 +0200 Subject: [PATCH 12/27] Use `externalincludedirs` instead of deprecated `sysincludedirs`. --- cmake_project.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 0802165..6ca6d6e 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -118,9 +118,9 @@ function m.generate(prj) -- include dirs - if #cfg.sysincludedirs > 0 then + if #cfg.externalincludedirs > 0 then _p('target_include_directories("%s" SYSTEM PRIVATE', prj.name) - for _, includedir in ipairs(cfg.sysincludedirs) do + for _, includedir in ipairs(cfg.externalincludedirs) do _x(1, '$<$:%s>', cmake.cfgname(cfg), includedir) end _p(')') From c781e846312fac03d92c31f7d9f356fa2ce05798 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Mon, 30 Jan 2023 19:33:35 +0100 Subject: [PATCH 13/27] Handle linkoptions for `sanitize { "Address", "Fuzzer" }`. --- cmake_project.lua | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 6ca6d6e..262f341 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -202,12 +202,24 @@ function m.generate(prj) all_link_options = all_link_options .. option .. " " end - if all_link_options ~= "" then + if all_link_options ~= "" or cfg.sanitize then _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) - _p(1, 'set_target_properties("%s" PROPERTIES LINK_FLAGS "%s")', prj.name, all_link_options) + if all_link_options ~= "" then + _p(1, 'set_target_properties("%s" PROPERTIES LINK_FLAGS "%s")', prj.name, all_link_options) + end + if cfg.sanitize then + _p(1, 'if (NOT MSVC)') + if table.contains(cfg.sanitize, "Address") then + _p(2, 'target_link_options("%s" PRIVATE "-fsanitize=address")', prj.name) + end + if table.contains(cfg.sanitize, "Fuzzer") then + _p(2, 'target_link_options("%s" PRIVATE "-fsanitize=fuzzer")', prj.name) + end + _p(1, 'endif()') + end _p('endif()') end - + if #toolset.getcflags(cfg) > 0 or #toolset.getcxxflags(cfg) > 0 then _p('target_compile_options("%s" PRIVATE', prj.name) From 69aecd3f115f1cd46e64698922bc6fe8107b5a9c Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Mon, 20 Feb 2023 20:31:42 +0100 Subject: [PATCH 14/27] cflags/cppflags handles both msvc and gcc toolset. --- cmake_project.lua | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 262f341..382bc78 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -221,15 +221,25 @@ function m.generate(prj) end if #toolset.getcflags(cfg) > 0 or #toolset.getcxxflags(cfg) > 0 then - _p('target_compile_options("%s" PRIVATE', prj.name) - - for _, flag in ipairs(toolset.getcflags(cfg)) do - _p(1, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + _p('if (MSVC)') + _p(1, 'target_compile_options("%s" PRIVATE', prj.name) + for _, flag in ipairs(p.tools.msc.getcflags(cfg)) do + _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) end - for _, flag in ipairs(toolset.getcxxflags(cfg)) do - _p(1, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + for _, flag in ipairs(p.tools.msc.getcxxflags(cfg)) do + _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) end - _p(')') + _p(1, ')') + _p('else()') + _p(1, 'target_compile_options("%s" PRIVATE', prj.name) + for _, flag in ipairs(p.tools.gcc.getcflags(cfg)) do + _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + end + for _, flag in ipairs(p.tools.gcc.getcxxflags(cfg)) do + _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + end + _p(1, ')') + _p('endif()') end -- C++ standard From f9c0cef66a97536ba92e15b347409e8c460247b7 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Wed, 22 Feb 2023 12:50:14 +0100 Subject: [PATCH 15/27] Fix single quote in messages (as premake-qt adds). --- cmake_project.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 382bc78..2909d67 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -21,6 +21,9 @@ local cmake = p.modules.cmake cmake.project = {} local m = cmake.project +function m.quote(s) -- handle single quote: required for "old" version of cmake + return premake.quote(s):gsub("'", " ") +end function m.getcompiler(cfg) local default = iif(cfg.system == p.WINDOWS, "msc", "clang") @@ -310,7 +313,7 @@ function m.generate(prj) -- so instead, use add_custom_target to run it before any rule (as obj) _p('add_custom_target(prebuild-%s', prj.name) if cfg.prebuildmessage then - local command = os.translateCommandsAndPaths("{ECHO} " .. premake.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location) + local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location) _p(' COMMAND %s', command) end local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location) @@ -324,7 +327,7 @@ function m.generate(prj) if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then _p('add_custom_command(TARGET %s POST_BUILD', prj.name) if cfg.postbuildmessage then - local command = os.translateCommandsAndPaths("{ECHO} " .. premake.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location) + local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location) _p(' COMMAND %s', command) end local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location) @@ -346,7 +349,7 @@ function m.generate(prj) _p('add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) if fileconfig.buildmessage then - _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. premake.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) + _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. m.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) end for _, command in ipairs(fileconfig.buildcommands) do _p(' COMMAND %s', os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) From 61667477e208d9aa1a87f2e7e62495fdf65ce809 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Wed, 8 Mar 2023 22:28:00 +0100 Subject: [PATCH 16/27] - Fix some multi-configuration issues. - Fix indentation. - Fix empty test for `cfg.sanitize`. --- cmake_project.lua | 130 ++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 2909d67..f9abec4 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -39,7 +39,7 @@ function m.files(prj) tree.traverse(tr, { onleaf = function(node, depth) - _p(depth, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) + _p(1, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) -- add generated files for cfg in project.eachconfig(prj) do @@ -48,7 +48,7 @@ function m.files(prj) if p.fileconfig.hasFileSettings(filecfg) then for _, output in ipairs(filecfg.buildoutputs) do - _p(depth, '"%s"', path.getrelative(prj.workspace.location, output)) + _p(1, '"%s"', path.getrelative(prj.workspace.location, output)) end break elseif rule then @@ -60,7 +60,7 @@ function m.files(prj) end local rulecfg = p.context.extent(rule, environ) for _, output in ipairs(rulecfg.buildoutputs) do - _p(depth, '"%s"', path.getrelative(prj.workspace.location, output)) + _p(1, '"%s"', path.getrelative(prj.workspace.location, output)) end break end @@ -117,74 +117,73 @@ function m.generate(prj) _p(2, 'LIBRARY_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) _p(2, 'RUNTIME_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) _p(1,')') - _p('endif()') -- include dirs if #cfg.externalincludedirs > 0 then - _p('target_include_directories("%s" SYSTEM PRIVATE', prj.name) + _p(1, 'target_include_directories("%s" SYSTEM PRIVATE', prj.name) for _, includedir in ipairs(cfg.externalincludedirs) do - _x(1, '$<$:%s>', cmake.cfgname(cfg), includedir) + _x(2, '"%s"', includedir) end - _p(')') + _p(1, ')') end if #cfg.includedirs > 0 then - _p('target_include_directories("%s" PRIVATE', prj.name) + _p(1, 'target_include_directories("%s" PRIVATE', prj.name) for _, includedir in ipairs(cfg.includedirs) do - _x(1, '$<$:%s>', cmake.cfgname(cfg), includedir) + _x(2, '"%s"', includedir) end - _p(')') + _p(1, ')') end if #cfg.forceincludes > 0 then - _p('if (MSVC)') - _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getforceincludes(cfg), "", "", " ")) - _p('else()') - _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getforceincludes(cfg), "", "", " ")) - _p('endif()') + _p(1, 'if (MSVC)') + _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getforceincludes(cfg), "", "", " ")) + _p(1, 'else()') + _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getforceincludes(cfg), "", "", " ")) + _p(1, 'endif()') end -- defines if #cfg.defines > 0 then - _p('target_compile_definitions("%s" PRIVATE', prj.name) + _p(1, 'target_compile_definitions("%s" PRIVATE', prj.name) for _, define in ipairs(cfg.defines) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), p.esc(define):gsub(' ', '\\ ')) + _p(2, '"%s"', p.esc(define):gsub(' ', '\\ ')) end - _p(')') + _p(1, ')') end -- lib dirs if #cfg.libdirs > 0 then - _p('target_link_directories("%s" PRIVATE', prj.name) + _p(1, 'target_link_directories("%s" PRIVATE', prj.name) for _, libdir in ipairs(cfg.libdirs) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), libdir) + _p(2, '"%s"', libdir) end - _p(')') + _p(1, ')') end -- libs local uselinkgroups = isclangorgcc and cfg.linkgroups == p.ON if uselinkgroups or # config.getlinks(cfg, "dependencies", "object") > 0 or #config.getlinks(cfg, "system", "fullpath") > 0 then - _p('target_link_libraries("%s"', prj.name) + _p(1, 'target_link_libraries("%s"', prj.name) -- Do not use toolset here as cmake needs to resolve dependency chains if uselinkgroups then - _p(1, '-Wl,--start-group') + _p(2, '-Wl,--start-group') end for a, link in ipairs(config.getlinks(cfg, "dependencies", "object")) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), link.project.name) + _p(2, '"%s"', link.project.name) end if uselinkgroups then -- System libraries don't depend on the project - _p(1, '-Wl,--end-group') - _p(1, '-Wl,--start-group') + _p(2, '-Wl,--end-group') + _p(2, '-Wl,--start-group') end for _, link in ipairs(config.getlinks(cfg, "system", "fullpath")) do - _p(1, '$<$:%s>', cmake.cfgname(cfg), link) + _p(2, '"%s"', link) end if uselinkgroups then - _p(1, '-Wl,--end-group') + _p(2, '-Wl,--end-group') end - _p(')') + _p(1, ')') end -- setting build options @@ -194,9 +193,9 @@ function m.generate(prj) end if all_build_options ~= "" then - _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) - _p(1, 'set_target_properties("%s" PROPERTIES COMPILE_FLAGS %s)', prj.name, all_build_options) - _p('endif()') + _p(1, 'if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) + _p(2, 'set_target_properties("%s" PROPERTIES COMPILE_FLAGS %s)', prj.name, all_build_options) + _p(1, 'endif()') end -- setting link options @@ -205,12 +204,11 @@ function m.generate(prj) all_link_options = all_link_options .. option .. " " end - if all_link_options ~= "" or cfg.sanitize then - _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) + if all_link_options ~= "" or (cfg.sanitize and #cfg.sanitize ~= 0) then if all_link_options ~= "" then _p(1, 'set_target_properties("%s" PROPERTIES LINK_FLAGS "%s")', prj.name, all_link_options) end - if cfg.sanitize then + if cfg.sanitize and #cfg.sanitize ~= 0 then _p(1, 'if (NOT MSVC)') if table.contains(cfg.sanitize, "Address") then _p(2, 'target_link_options("%s" PRIVATE "-fsanitize=address")', prj.name) @@ -220,29 +218,28 @@ function m.generate(prj) end _p(1, 'endif()') end - _p('endif()') end if #toolset.getcflags(cfg) > 0 or #toolset.getcxxflags(cfg) > 0 then - _p('if (MSVC)') - _p(1, 'target_compile_options("%s" PRIVATE', prj.name) + _p(1, 'if (MSVC)') + _p(2, 'target_compile_options("%s" PRIVATE', prj.name) for _, flag in ipairs(p.tools.msc.getcflags(cfg)) do - _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + _p(3, '$<$:%s>', flag) end for _, flag in ipairs(p.tools.msc.getcxxflags(cfg)) do - _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + _p(3, '$<$:%s>', flag) end - _p(1, ')') - _p('else()') - _p(1, 'target_compile_options("%s" PRIVATE', prj.name) + _p(2, ')') + _p(1, 'else()') + _p(2, 'target_compile_options("%s" PRIVATE', prj.name) for _, flag in ipairs(p.tools.gcc.getcflags(cfg)) do - _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + _p(3, '$<$:%s>', flag) end for _, flag in ipairs(p.tools.gcc.getcxxflags(cfg)) do - _p(2, '$<$,$>:%s>', cmake.cfgname(cfg), flag) + _p(3, '$<$:%s>', flag) end - _p(1, ')') - _p('endif()') + _p(2, ')') + _p(1, 'endif()') end -- C++ standard @@ -264,7 +261,6 @@ function m.generate(prj) local pic = iif(cfg.pic == 'On', 'True', 'False') local lto = iif(cfg.flags.LinkTimeOptimization, 'True', 'False') - _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) _p(1, 'set_target_properties("%s" PROPERTIES', prj.name) _p(2, 'CXX_STANDARD %s', standard[cfg.cppdialect]) _p(2, 'CXX_STANDARD_REQUIRED YES') @@ -272,7 +268,6 @@ function m.generate(prj) _p(2, 'POSITION_INDEPENDENT_CODE %s', pic) _p(2, 'INTERPROCEDURAL_OPTIMIZATION %s', lto) _p(1, ')') - _p('endif()') end -- precompiled headers @@ -302,41 +297,38 @@ function m.generate(prj) pch = project.getrelative(cfg.project, path.getabsolute(pch)) end - _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) - _p('target_precompile_headers("%s" PUBLIC %s)', prj.name, pch) - _p('endif()') + _p(1, 'target_precompile_headers("%s" PUBLIC "%s")', prj.name, pch) end -- pre/post buildcommands if cfg.prebuildmessage or #cfg.prebuildcommands > 0 then -- add_custom_command PRE_BUILD runs just before generating the target -- so instead, use add_custom_target to run it before any rule (as obj) - _p('add_custom_target(prebuild-%s', prj.name) + _p(1, 'add_custom_target(prebuild-%s', prj.name) if cfg.prebuildmessage then local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location) - _p(' COMMAND %s', command) + _p(2, 'COMMAND %s', command) end local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location) for _, command in ipairs(commands) do - _p(' COMMAND %s', command) + _p(2, 'COMMAND %s', command) end - _p(')') - _p('add_dependencies(%s prebuild-%s)', prj.name, prj.name) + _p(1, ')') + _p(1, 'add_dependencies(%s prebuild-%s)', prj.name, prj.name) end if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then - _p('add_custom_command(TARGET %s POST_BUILD', prj.name) + _p(1, 'add_custom_command(TARGET %s POST_BUILD', prj.name) if cfg.postbuildmessage then local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location) - _p(' COMMAND %s', command) + _p(2, 'COMMAND %s', command) end local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location) for _, command in ipairs(commands) do - _p(' COMMAND %s', command) + _p(2, 'COMMAND %s', command) end - _p(')') + _p(1, ')') end - -- custom command local function addCustomCommand(fileconfig, filename) if #fileconfig.buildcommands == 0 or #fileconfig.buildoutputs == 0 then @@ -345,22 +337,22 @@ function m.generate(prj) local custom_output_directories = table.unique(table.translate(fileconfig.buildoutputs, function(output) return project.getrelative(cfg.project, path.getdirectory(output)) end)) -- Alternative would be to add 'COMMAND ${CMAKE_COMMAND} -E make_directory %s' to below add_custom_command - _p('file(MAKE_DIRECTORY %s)', table.implode(custom_output_directories, "", "", " ")) + _p(1, 'file(MAKE_DIRECTORY %s)', table.implode(custom_output_directories, "", "", " ")) - _p('add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) + _p(1, 'add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) if fileconfig.buildmessage then - _p(' COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. m.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) + _p(2, 'COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. m.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) end for _, command in ipairs(fileconfig.buildcommands) do - _p(' COMMAND %s', os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) + _p(2, 'COMMAND %s', os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) end if filename ~= "" and #fileconfig.buildinputs ~= 0 then filename = filename .. " " end if filename ~= "" or #fileconfig.buildinputs ~= 0 then - _p(' DEPENDS %s', filename .. table.implode(fileconfig.buildinputs,"",""," ")) + _p(2, 'DEPENDS %s', filename .. table.implode(fileconfig.buildinputs,"",""," ")) end - _p(')') + _p(1, ')') end local tr = project.getsourcetree(cfg.project) p.tree.traverse(tr, { @@ -383,6 +375,8 @@ function m.generate(prj) end }) addCustomCommand(cfg, "") + _p('endif()') + _p('') end -- restore path.getDefaultSeparator = oldGetDefaultSeparator From 859455655fedfbf1e02305a68788d779050823a9 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Wed, 5 Apr 2023 22:16:57 +0200 Subject: [PATCH 17/27] Add generated files only when `filecfg.compilebuildoutputs` is true. --- cmake_project.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index f9abec4..8b93b19 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -47,8 +47,10 @@ function m.files(prj) local rule = p.global.getRuleForFile(node.name, prj.rules) if p.fileconfig.hasFileSettings(filecfg) then - for _, output in ipairs(filecfg.buildoutputs) do - _p(1, '"%s"', path.getrelative(prj.workspace.location, output)) + if filecfg.compilebuildoutputs then + for _, output in ipairs(filecfg.buildoutputs) do + _p(1, '"%s"', path.getrelative(prj.workspace.location, output)) + end end break elseif rule then From cdefe421a00c2f7cef62775cb64f613212e6e886 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Thu, 6 Apr 2023 23:19:02 +0200 Subject: [PATCH 18/27] Add generated file as dependencies of app. --- cmake_project.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index 8b93b19..7b056bf 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -355,6 +355,11 @@ function m.generate(prj) _p(2, 'DEPENDS %s', filename .. table.implode(fileconfig.buildinputs,"",""," ")) end _p(1, ')') + if not fileconfig.compilebuildoutputs then + local target_name = 'CUSTOM_TARGET_' .. filename:gsub('/', '_'):gsub('\\', '_') + _p(1, 'add_custom_target(%s DEPENDS %s)', target_name, table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) + _p(1, 'add_dependencies(%s %s)', prj.name, target_name) + end end local tr = project.getsourcetree(cfg.project) p.tree.traverse(tr, { From f0ef9603da29fce22d624414e148e40373bdd7c4 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Sun, 16 Apr 2023 22:33:19 +0200 Subject: [PATCH 19/27] Add support of `frameworkdirs` and `includedirsafter` --- cmake_project.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index 7b056bf..43fa395 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -137,6 +137,14 @@ function m.generate(prj) _p(1, ')') end + if #cfg.frameworkdirs > 0 or #cfg.includedirsafter > 0 then + _p(1, 'if (MSVC)') + _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getincludedirs(cfg, {}, {}, cfg.frameworkdirs, cfg.includedirsafter), "", "", " ")) + _p(1, 'else()') + _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getincludedirs(cfg, {}, {}, cfg.frameworkdirs, cfg.includedirsafter), "", "", " ")) + _p(1, 'endif()') + end + if #cfg.forceincludes > 0 then _p(1, 'if (MSVC)') _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getforceincludes(cfg), "", "", " ")) From 0ba0437f6a9a02d546d0e63a12af57798788e8c2 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Thu, 27 Apr 2023 23:23:40 +0200 Subject: [PATCH 20/27] Add prelink support. --- cmake_project.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index 43fa395..8111adb 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -327,6 +327,19 @@ function m.generate(prj) _p(1, 'add_dependencies(%s prebuild-%s)', prj.name, prj.name) end + if cfg.prelinkmessage or #cfg.prelinkcommands > 0 then + _p(1, 'add_custom_command(TARGET %s PRE_LINK', prj.name) + if cfg.prelinkmessage then + local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prelinkmessage), cfg.project.basedir, cfg.project.location) + _p(2, 'COMMAND %s', command) + end + local commands = os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location) + for _, command in ipairs(commands) do + _p(2, 'COMMAND %s', command) + end + _p(1, ')') + end + if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then _p(1, 'add_custom_command(TARGET %s POST_BUILD', prj.name) if cfg.postbuildmessage then From 9f8d1b803c3c6c69aac49b585878c18eda162275 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Fri, 19 May 2023 21:39:21 +0200 Subject: [PATCH 21/27] Handle `flags {"ExcludeFromBuild"}`. --- cmake_project.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index 8111adb..196695c 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -39,6 +39,9 @@ function m.files(prj) tree.traverse(tr, { onleaf = function(node, depth) + if node.flags.ExcludeFromBuild then + return + end _p(1, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) -- add generated files From f6e249b906805b9d83869c22acb7b82d90f83349 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Fri, 19 May 2023 22:38:30 +0200 Subject: [PATCH 22/27] Fix generated files in different directory depending of config. --- cmake_project.lua | 71 ++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 196695c..4ea4d85 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -34,42 +34,51 @@ function m.getcompiler(cfg) return toolset end -function m.files(prj) - local tr = project.getsourcetree(prj) - tree.traverse(tr, { - onleaf = function(node, depth) - +function m.generated_files(prj) + for cfg in project.eachconfig(prj) do + _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) + _p(1, 'set(GENERATED_FILES') + table.foreachi(prj._.files, function(node) if node.flags.ExcludeFromBuild then return end - _p(1, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) - - -- add generated files - for cfg in project.eachconfig(prj) do - local filecfg = p.fileconfig.getconfig(node, cfg) - local rule = p.global.getRuleForFile(node.name, prj.rules) + local filecfg = p.fileconfig.getconfig(node, cfg) + local rule = p.global.getRuleForFile(node.name, prj.rules) - if p.fileconfig.hasFileSettings(filecfg) then - if filecfg.compilebuildoutputs then - for _, output in ipairs(filecfg.buildoutputs) do - _p(1, '"%s"', path.getrelative(prj.workspace.location, output)) - end + if p.fileconfig.hasFileSettings(filecfg) then + if filecfg.compilebuildoutputs then + for _, output in ipairs(filecfg.buildoutputs) do + _p(2, '"%s"', path.getrelative(prj.workspace.location, output)) end - break - elseif rule then - local environ = table.shallowcopy(filecfg.environ) + end + elseif rule then + local environ = table.shallowcopy(filecfg.environ) - if rule.propertydefinition then - p.rule.prepareEnvironment(rule, environ, cfg) - p.rule.prepareEnvironment(rule, environ, filecfg) - end - local rulecfg = p.context.extent(rule, environ) - for _, output in ipairs(rulecfg.buildoutputs) do - _p(1, '"%s"', path.getrelative(prj.workspace.location, output)) - end - break + if rule.propertydefinition then + p.rule.prepareEnvironment(rule, environ, cfg) + p.rule.prepareEnvironment(rule, environ, filecfg) end + local rulecfg = p.context.extent(rule, environ) + for _, output in ipairs(rulecfg.buildoutputs) do + _p(2, '"%s"', path.getrelative(prj.workspace.location, output)) + end + end + end) + _p(1, ')') + _p(0, 'endif()') + end +end + + +function m.files(prj) + local tr = project.getsourcetree(prj) + + tree.traverse(tr, { + onleaf = function(node, depth) + if node.flags.ExcludeFromBuild or node.generated then + return end + _p(1, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) end }) end @@ -87,6 +96,9 @@ function m.generate(prj) local oldGetDefaultSeparator = path.getDefaultSeparator path.getDefaultSeparator = function() return "/" end + if prj.hasGeneratedFiles then + m.generated_files(prj) + end if prj.kind == 'StaticLib' then _p('add_library("%s" STATIC', prj.name) @@ -99,6 +111,9 @@ function m.generate(prj) _p('add_executable("%s"', prj.name) end m.files(prj) + if prj.hasGeneratedFiles then + _p(1, '${GENERATED_FILES}') + end _p(')') for cfg in project.eachconfig(prj) do From 65d232f1bb49c8a53cb3ffbc3c51eb1b3637eeb3 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Mon, 29 May 2023 14:51:10 +0200 Subject: [PATCH 23/27] Add support to `undefines`. --- cmake_project.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmake_project.lua b/cmake_project.lua index 4ea4d85..03c4a1b 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -180,6 +180,14 @@ function m.generate(prj) _p(1, ')') end + if #cfg.undefines > 0 then + _p(1, 'if (MSVC)') + _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getundefines(cfg.undefines), "", "", " ")) + _p(1, 'else()') + _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getundefines(cfg.undefines), "", "", " ")) + _p(1, 'endif()') + end + -- lib dirs if #cfg.libdirs > 0 then _p(1, 'target_link_directories("%s" PRIVATE', prj.name) From d71320960df7ef47c4e9da31572015b91208a4bc Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Tue, 25 Jun 2024 16:01:04 +0200 Subject: [PATCH 24/27] Default toolset depends on target OS and fix toolset with version --- _preload.lua | 10 +++++++++- cmake_project.lua | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/_preload.lua b/_preload.lua index a035527..c64a69e 100644 --- a/_preload.lua +++ b/_preload.lua @@ -20,6 +20,14 @@ p.api.register { kind = "string", } +local default_toolset_map = { + ["windows"] = "msc-v142", -- Visual Studio 2019 + ["macosx"] = "clang", + ["linux"] = "gcc", + ["_"] = "gcc", -- default +} +local default_toolset = default_toolset_map[os.target()] or default_toolset_map["_"] + newaction { -- Metadata for the command line and help system @@ -27,7 +35,7 @@ newaction trigger = "cmake", shortname = "CMake", description = "Generate CMake file", - toolset = "clang", + toolset = default_toolset, -- The capabilities of this action diff --git a/cmake_project.lua b/cmake_project.lua index 03c4a1b..c9ff68f 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -27,7 +27,7 @@ end function m.getcompiler(cfg) local default = iif(cfg.system == p.WINDOWS, "msc", "clang") - local toolset = p.tools[_OPTIONS.cc or cfg.toolset or default] + local toolset, toolset_version = p.tools.canonical(_OPTIONS.cc or cfg.toolset or default) if not toolset then error("Invalid toolset '" + (_OPTIONS.cc or cfg.toolset) + "'") end From 1cbc9e201eefd75a3f554b6ca2e03016e930145f Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Wed, 17 Jul 2024 17:04:02 +0200 Subject: [PATCH 25/27] Handle multi-config generators --- cmake_project.lua | 723 +++++++++++++++++++++++++++++----------------- 1 file changed, 451 insertions(+), 272 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index c9ff68f..736e4c7 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -8,8 +8,9 @@ -- Yehonatan Ballas -- Joel Linn -- UndefinedVertex +-- Joris Dauphin -- Created: 2013/05/06 --- Copyright: (c) 2008-2020 Jason Perkins and the Premake project +-- Copyright: (c) 2008-2024 Jason Perkins and the Premake project -- local p = premake @@ -21,8 +22,27 @@ local cmake = p.modules.cmake cmake.project = {} local m = cmake.project +function m.esc(s) + if type(s) == "table" then + return table.translate(s, m.esc) + end + s, _ = s:gsub('\\', '\\\\') + s, _ = s:gsub('"', '\\"') + return s +end + +function m.unquote(s) + if type(s) == "table" then + return table.translate(s, m.unquote) + end + s, _ = s:gsub('"', '') + return s +end + + function m.quote(s) -- handle single quote: required for "old" version of cmake - return premake.quote(s):gsub("'", " ") + s, _ = premake.quote(s):gsub("'", " ") + return s end function m.getcompiler(cfg) @@ -34,53 +54,99 @@ function m.getcompiler(cfg) return toolset end -function m.generated_files(prj) - for cfg in project.eachconfig(prj) do - _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) - _p(1, 'set(GENERATED_FILES') - table.foreachi(prj._.files, function(node) - if node.flags.ExcludeFromBuild then - return - end - local filecfg = p.fileconfig.getconfig(node, cfg) - local rule = p.global.getRuleForFile(node.name, prj.rules) +function m.files(cfg) + local prj = cfg.project + local files = {} - if p.fileconfig.hasFileSettings(filecfg) then - if filecfg.compilebuildoutputs then - for _, output in ipairs(filecfg.buildoutputs) do - _p(2, '"%s"', path.getrelative(prj.workspace.location, output)) - end - end - elseif rule then - local environ = table.shallowcopy(filecfg.environ) + table.foreachi(prj._.files, function(node) + if node.flags.ExcludeFromBuild then + return + end + local filecfg = p.fileconfig.getconfig(node, cfg) + local rule = p.global.getRuleForFile(node.name, prj.rules) - if rule.propertydefinition then - p.rule.prepareEnvironment(rule, environ, cfg) - p.rule.prepareEnvironment(rule, environ, filecfg) - end - local rulecfg = p.context.extent(rule, environ) - for _, output in ipairs(rulecfg.buildoutputs) do - _p(2, '"%s"', path.getrelative(prj.workspace.location, output)) + if p.fileconfig.hasFileSettings(filecfg) then + if filecfg.compilebuildoutputs then + for _, output in ipairs(filecfg.buildoutputs) do + table.insert(files, string.format('%s', path.getrelative(prj.workspace.location, output))) end end - end) - _p(1, ')') - _p(0, 'endif()') - end -end + elseif rule then + local environ = table.shallowcopy(filecfg.environ) + if rule.propertydefinition then + p.rule.prepareEnvironment(rule, environ, cfg) + p.rule.prepareEnvironment(rule, environ, filecfg) + end + local rulecfg = p.context.extent(rule, environ) + for _, output in ipairs(rulecfg.buildoutputs) do + table.insert(files, string.format('%s', path.getrelative(prj.workspace.location, output))) + end + elseif not node.generated then + table.insert(files, string.format('%s', path.getrelative(prj.workspace.location, node.abspath))) + end + end) + return files +end -function m.files(prj) - local tr = project.getsourcetree(prj) +local function is_empty(t) + for _, _ in pairs(t) do + return false + end + return true +end - tree.traverse(tr, { - onleaf = function(node, depth) - if node.flags.ExcludeFromBuild or node.generated then - return - end - _p(1, '"%s"', path.getrelative(prj.workspace.location, node.abspath)) +local one_expression = "one_expression" +local table_expression = "table_expression" +local function generator_expression(prj, callback, mode) + local common = nil + local by_cfg = {} + for cfg in project.eachconfig(prj) do + local settings = callback(cfg) + + if not common then + common = table.arraycopy(settings) + else + common = table.intersect(common, settings) end - }) + by_cfg[cfg] = settings + end + for cfg in project.eachconfig(prj) do + by_cfg[cfg] = table.difference(by_cfg[cfg], common) + if is_empty(by_cfg[cfg]) then + by_cfg[cfg] = nil + end + end + common_str = table.implode(common or {}, "", "", " ") + if is_empty(by_cfg) then + if mode == table_expression then + return common, true + else + return common_str, true + end + end + if #common_str > 0 then + common_str = common_str .. " " + end + if mode == one_expression then + local res = '' + local suffix = '' + for cfg, settings in pairs(by_cfg) do + res = res .. string.format('$,%s,', cmake.cfgname(cfg), m.esc(common_str .. table.implode(settings, "", "", " "))) + suffix = suffix .. '>' + end + return res .. suffix, false + else + local res = {} + for cfg, settings in pairs(by_cfg) do + res = table.join(res, table.translate(settings, function(setting) return string.format('$<$:%s>', cmake.cfgname(cfg), m.esc(setting)) end)) + end + if mode == table_expression then + return table.join(common, res), false + else + return common_str .. table.implode(res, "", "", " "), false + end + end end -- @@ -96,10 +162,6 @@ function m.generate(prj) local oldGetDefaultSeparator = path.getDefaultSeparator path.getDefaultSeparator = function() return "/" end - if prj.hasGeneratedFiles then - m.generated_files(prj) - end - if prj.kind == 'StaticLib' then _p('add_library("%s" STATIC', prj.name) elseif prj.kind == 'SharedLib' then @@ -110,203 +172,240 @@ function m.generate(prj) end _p('add_executable("%s"', prj.name) end - m.files(prj) - if prj.hasGeneratedFiles then - _p(1, '${GENERATED_FILES}') + for _, file in ipairs(generator_expression(prj, m.files, table_expression)) do + _p(1, '%s', file); end _p(')') - - for cfg in project.eachconfig(prj) do - local toolset = m.getcompiler(cfg) - local isclangorgcc = toolset == p.tools.clang or toolset == p.tools.gcc - _p('if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) - -- dependencies - local dependencies = project.getdependencies(prj) - if #dependencies > 0 then - _p(1, 'add_dependencies("%s"', prj.name) - for _, dependency in ipairs(dependencies) do - _p(2, '"%s"', dependency.name) - end - _p(1,')') + _p(0, 'set_target_properties("%s" PROPERTIES OUTPUT_NAME %s)', prj.name, generator_expression(prj, function(cfg) return {cfg.buildtarget.basename} end, one_expression)) + -- output dir + _p(0, 'set_target_properties("%s" PROPERTIES', prj.name) + for cfg in project.eachconfig(prj) do + -- Multi-configuration generators appends a per-configuration subdirectory + -- to the specified directory (unless a generator expression is used) + -- for XXX_OUTPUT_DIRECTORY but not for XXX_OUTPUT_DIRECTORY_ + _p(1, 'ARCHIVE_OUTPUT_DIRECTORY_%s "%s"', cmake.cfgname(cfg):upper(), path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) + _p(1, 'LIBRARY_OUTPUT_DIRECTORY_%s "%s"', cmake.cfgname(cfg):upper(), path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) + _p(1, 'RUNTIME_OUTPUT_DIRECTORY_%s "%s"', cmake.cfgname(cfg):upper(), path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) + end + _p(0, ')') + + -- dependencies + local dependencies = project.getdependencies(prj) + if #dependencies > 0 then + _p(0, 'add_dependencies("%s"', prj.name) + for _, dependency in ipairs(dependencies) do + _p(1, '"%s"', dependency.name) end + _p(0,')') + end - -- output dir - _p(1,'set_target_properties("%s" PROPERTIES', prj.name) - _p(2, 'OUTPUT_NAME "%s"', cfg.buildtarget.basename) - _p(2, 'ARCHIVE_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) - _p(2, 'LIBRARY_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) - _p(2, 'RUNTIME_OUTPUT_DIRECTORY "%s"', path.getrelative(prj.workspace.location, cfg.buildtarget.directory)) - _p(1,')') - - -- include dirs - - if #cfg.externalincludedirs > 0 then - _p(1, 'target_include_directories("%s" SYSTEM PRIVATE', prj.name) - for _, includedir in ipairs(cfg.externalincludedirs) do - _x(2, '"%s"', includedir) - end - _p(1, ')') + -- include dirs + local externalincludedirs = generator_expression(prj, function(cfg) return cfg.externalincludedirs end, table_expression) + if #externalincludedirs > 0 then + _p(0, 'target_include_directories("%s" SYSTEM PRIVATE', prj.name) + for _, dir in ipairs(externalincludedirs) do + _p(1, '%s', dir) end - if #cfg.includedirs > 0 then - _p(1, 'target_include_directories("%s" PRIVATE', prj.name) - for _, includedir in ipairs(cfg.includedirs) do - _x(2, '"%s"', includedir) - end - _p(1, ')') + _p(0, ')') + end + local includedirs = generator_expression(prj, function(cfg) return cfg.includedirs end, table_expression) + if #includedirs > 0 then + _p(0, 'target_include_directories("%s" PRIVATE', prj.name) + for _, dir in ipairs(includedirs) do + _p(1, '%s', dir) end + _p(0, ')') + end + + local msvc_frameworkdirs = generator_expression(prj, function(cfg) return p.tools.msc.getincludedirs(cfg, {}, {}, cfg.frameworkdirs, cfg.includedirsafter) end) + local gcc_frameworkdirs = generator_expression(prj, function(cfg) return p.tools.gcc.getincludedirs(cfg, {}, {}, cfg.frameworkdirs, cfg.includedirsafter) end) + + if #msvc_frameworkdirs > 0 or #gcc_frameworkdirs > 0 then + _p(0, 'if (MSVC)') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, msvc_frameworkdirs) + _p(0, 'else()') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, gcc_frameworkdirs) + _p(0, 'endif()') + end - if #cfg.frameworkdirs > 0 or #cfg.includedirsafter > 0 then - _p(1, 'if (MSVC)') - _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getincludedirs(cfg, {}, {}, cfg.frameworkdirs, cfg.includedirsafter), "", "", " ")) - _p(1, 'else()') - _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getincludedirs(cfg, {}, {}, cfg.frameworkdirs, cfg.includedirsafter), "", "", " ")) - _p(1, 'endif()') + local msvc_forceincludes = generator_expression(prj, function(cfg) return p.tools.msc.getforceincludes(cfg) end) + local gcc_forceincludes = generator_expression(prj, function(cfg) return p.tools.gcc.getforceincludes(cfg) end) + if #msvc_forceincludes > 0 or #gcc_forceincludes > 0 then + _p(0, '# force include') + _p(0, 'if (MSVC)') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, msvc_forceincludes) + _p(0, 'else()') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, gcc_forceincludes) + _p(0, 'endif()') + end + + -- defines + local defines = generator_expression(prj, function(cfg) return m.esc(cfg.defines) end, table_expression) --p.esc(define):gsub(' ', '\\ ') + if #defines > 0 then + _p(0, 'target_compile_definitions("%s" PRIVATE', prj.name) + for _, define in ipairs(defines) do + _p(1, '%s', define) end + _p(0, ')') + end - if #cfg.forceincludes > 0 then - _p(1, 'if (MSVC)') - _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getforceincludes(cfg), "", "", " ")) - _p(1, 'else()') - _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getforceincludes(cfg), "", "", " ")) - _p(1, 'endif()') + local msvc_undefines = generator_expression(prj, function(cfg) return p.tools.msc.getundefines(cfg.undefines) end) + local gcc_undefines = generator_expression(prj, function(cfg) return p.tools.gcc.getundefines(cfg.undefines) end) + + if #msvc_undefines > 0 or #gcc_undefines > 0 then + _p(0, 'if (MSVC)') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, msvc_undefines) + _p(0, 'else()') + _p(1, 'target_compile_options("%s" PRIVATE %s)', prj.name, gcc_undefines) + _p(0, 'endif()') + end + + -- setting build options + local all_build_options = generator_expression(prj, function(cfg) return m.unquote(cfg.buildoptions) end, table_expression) + if #all_build_options > 0 then + _p(0, 'target_compile_options("%s" PRIVATE', prj.name) + for _, option in ipairs(all_build_options) do + _p(1, '%s', option) end + _p(0, ')') + end - -- defines - if #cfg.defines > 0 then - _p(1, 'target_compile_definitions("%s" PRIVATE', prj.name) - for _, define in ipairs(cfg.defines) do - _p(2, '"%s"', p.esc(define):gsub(' ', '\\ ')) - end - _p(1, ')') + -- C++ standard + -- only need to configure it specified + local cppdialect = generator_expression(prj, function(cfg) + if (cfg.cppdialect ~= nil and cfg.cppdialect ~= '') or cfg.cppdialect == 'Default' then + local standard = { + ["C++98"] = 98, + ["C++11"] = 11, + ["C++14"] = 14, + ["C++17"] = 17, + ["C++20"] = 20, + ["gnu++98"] = 98, + ["gnu++11"] = 11, + ["gnu++14"] = 14, + ["gnu++17"] = 17, + ["gnu++20"] = 20 + } + return { tostring(standard[cfg.cppdialect]) } end + return {} + end, one_expression) + if #cppdialect > 0 then + local extension = generator_expression(prj, function(cfg) return iif(cfg.cppdialect:find('^gnu') == nil, {'NO'}, {'YES'}) end, one_expression) + local pic = generator_expression(prj, function(cfg) return iif(cfg.pic == 'On', {'True'}, {'False'}) end, one_expression) + local lto = generator_expression(prj, function(cfg) return iif(cfg.flags.LinkTimeOptimization, {'True'}, {'False'}) end, one_expression) + _p(0, 'set_target_properties("%s" PROPERTIES', prj.name) + _p(1, 'CXX_STANDARD %s', cppdialect) + _p(1, 'CXX_STANDARD_REQUIRED YES') + _p(1, 'CXX_EXTENSIONS %s', extension) + _p(1, 'POSITION_INDEPENDENT_CODE %s', pic) + _p(1, 'INTERPROCEDURAL_OPTIMIZATION %s', lto) + _p(0, ')') + end - if #cfg.undefines > 0 then - _p(1, 'if (MSVC)') - _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.msc.getundefines(cfg.undefines), "", "", " ")) - _p(1, 'else()') - _p(2, 'target_compile_options("%s" PRIVATE %s)', prj.name, table.implode(p.tools.gcc.getundefines(cfg.undefines), "", "", " ")) - _p(1, 'endif()') + -- CFLAGS/CXXFLAGS + local msvc_cflags = generator_expression(prj, function(cfg) return table.translate(p.tools.msc.getcflags(cfg), function(s) return string.format('$<$:%s>', s) end) end, table_expression) + local msvc_cxxflags = generator_expression(prj, function(cfg) return table.translate(p.tools.msc.getcxxflags(cfg), function(s) return string.format('$<$:%s>', s) end) end, table_expression) + local gcc_cflags = generator_expression(prj, function(cfg) return table.translate(p.tools.gcc.getcflags(cfg), function(s) return string.format('$<$>,$>:%s>', s) end) end, table_expression) + local gcc_cxxflags = generator_expression(prj, function(cfg) return table.translate(p.tools.gcc.getcxxflags(cfg), function(s) return string.format('$<$>,$>:%s>', s) end) end, table_expression) + + if #msvc_cflags > 0 or #msvc_cxxflags > 0 or #gcc_cflags > 0 or #gcc_cxxflags > 0 then + _p(0, 'target_compile_options("%s" PRIVATE', prj.name) + for _, flag in ipairs(msvc_cflags) do + _p(1, flag) + end + for _, flag in ipairs(msvc_cxxflags) do + _p(1, flag) end + for _, flag in ipairs(gcc_cflags) do + _p(1, flag) + end + for _, flag in ipairs(gcc_cxxflags) do + _p(1, flag) + end + _p(0, ')') + end - -- lib dirs - if #cfg.libdirs > 0 then - _p(1, 'target_link_directories("%s" PRIVATE', prj.name) - for _, libdir in ipairs(cfg.libdirs) do - _p(2, '"%s"', libdir) - end - _p(1, ')') + -- lib dirs + local libdirs = generator_expression(prj, function(cfg) return cfg.libdirs end, table_expression) + if #libdirs > 0 then + _p(0, 'target_link_directories("%s" PRIVATE', prj.name) + for _, libdir in ipairs(libdirs) do + _p(1, '"%s"', libdir) end + _p(0, ')') + end - -- libs + -- libs + local libs = generator_expression(prj, function(cfg) + local toolset = m.getcompiler(cfg) + local isclangorgcc = toolset == p.tools.clang or toolset == p.tools.gcc local uselinkgroups = isclangorgcc and cfg.linkgroups == p.ON - if uselinkgroups or # config.getlinks(cfg, "dependencies", "object") > 0 or #config.getlinks(cfg, "system", "fullpath") > 0 then - _p(1, 'target_link_libraries("%s"', prj.name) + local res = {} + if uselinkgroups or #config.getlinks(cfg, "dependencies", "object") > 0 or #config.getlinks(cfg, "system", "fullpath") > 0 then -- Do not use toolset here as cmake needs to resolve dependency chains if uselinkgroups then - _p(2, '-Wl,--start-group') + table.insert(res, '-Wl,--start-group') end for a, link in ipairs(config.getlinks(cfg, "dependencies", "object")) do - _p(2, '"%s"', link.project.name) + table.insert(res, link.project.name) end if uselinkgroups then -- System libraries don't depend on the project - _p(2, '-Wl,--end-group') - _p(2, '-Wl,--start-group') + table.insert(res, '-Wl,--end-group') + table.insert(res, '-Wl,--start-group') end for _, link in ipairs(config.getlinks(cfg, "system", "fullpath")) do - _p(2, '"%s"', link) + table.insert(res, link) end if uselinkgroups then - _p(2, '-Wl,--end-group') + table.insert(res, '-Wl,--end-group') end - _p(1, ')') - end - - -- setting build options - all_build_options = "" - for _, option in ipairs(cfg.buildoptions) do - all_build_options = all_build_options .. option .. " " + return res end - - if all_build_options ~= "" then - _p(1, 'if(CMAKE_BUILD_TYPE STREQUAL %s)', cmake.cfgname(cfg)) - _p(2, 'set_target_properties("%s" PROPERTIES COMPILE_FLAGS %s)', prj.name, all_build_options) - _p(1, 'endif()') + return {} + end, table_expression) + if #libs > 0 then + _p(0, 'target_link_libraries("%s"', prj.name) + for _, lib in ipairs(libs) do + _p(1, '%s', lib) end + _p(0, ')') + end - -- setting link options - all_link_options = "" - for _, option in ipairs(cfg.linkoptions) do - all_link_options = all_link_options .. option .. " " + -- setting link options + local all_link_options = generator_expression(prj, function(cfg) + local toolset = m.getcompiler(cfg) + return table.join(toolset.getldflags(cfg), cfg.linkoptions) end, table_expression) + if #all_link_options > 0 then + _p(0, 'target_link_options("%s" PRIVATE', prj.name) + for _, link_option in ipairs(all_link_options) do + _p(1, '%s', link_option) end - - if all_link_options ~= "" or (cfg.sanitize and #cfg.sanitize ~= 0) then - if all_link_options ~= "" then - _p(1, 'set_target_properties("%s" PROPERTIES LINK_FLAGS "%s")', prj.name, all_link_options) - end - if cfg.sanitize and #cfg.sanitize ~= 0 then - _p(1, 'if (NOT MSVC)') - if table.contains(cfg.sanitize, "Address") then - _p(2, 'target_link_options("%s" PRIVATE "-fsanitize=address")', prj.name) - end - if table.contains(cfg.sanitize, "Fuzzer") then - _p(2, 'target_link_options("%s" PRIVATE "-fsanitize=fuzzer")', prj.name) - end - _p(1, 'endif()') - end + _p(0, ')') + end + local sanitize_addresss_options = generator_expression(prj, function(cfg) + if cfg.sanitize and #cfg.sanitize ~= 0 and table.contains(cfg.sanitize, "Address") then + return {'$<$>:-fsanitize=address>'} end - - if #toolset.getcflags(cfg) > 0 or #toolset.getcxxflags(cfg) > 0 then - _p(1, 'if (MSVC)') - _p(2, 'target_compile_options("%s" PRIVATE', prj.name) - for _, flag in ipairs(p.tools.msc.getcflags(cfg)) do - _p(3, '$<$:%s>', flag) - end - for _, flag in ipairs(p.tools.msc.getcxxflags(cfg)) do - _p(3, '$<$:%s>', flag) - end - _p(2, ')') - _p(1, 'else()') - _p(2, 'target_compile_options("%s" PRIVATE', prj.name) - for _, flag in ipairs(p.tools.gcc.getcflags(cfg)) do - _p(3, '$<$:%s>', flag) - end - for _, flag in ipairs(p.tools.gcc.getcxxflags(cfg)) do - _p(3, '$<$:%s>', flag) - end - _p(2, ')') - _p(1, 'endif()') + return {} + end, one_expression) + local sanitize_fuzzer_options = generator_expression(prj, function(cfg) + if cfg.sanitize and #cfg.sanitize ~= 0 and table.contains(cfg.sanitize, "Fuzzer") then + return {'$<$>:-fsanitize=fuzzer>'} end + return {} + end, one_expression) - -- C++ standard - -- only need to configure it specified - if (cfg.cppdialect ~= nil and cfg.cppdialect ~= '') or cfg.cppdialect == 'Default' then - local standard = {} - standard["C++98"] = 98 - standard["C++11"] = 11 - standard["C++14"] = 14 - standard["C++17"] = 17 - standard["C++20"] = 20 - standard["gnu++98"] = 98 - standard["gnu++11"] = 11 - standard["gnu++14"] = 14 - standard["gnu++17"] = 17 - standard["gnu++20"] = 20 - - local extentions = iif(cfg.cppdialect:find('^gnu') == nil, 'NO', 'YES') - local pic = iif(cfg.pic == 'On', 'True', 'False') - local lto = iif(cfg.flags.LinkTimeOptimization, 'True', 'False') - - _p(1, 'set_target_properties("%s" PROPERTIES', prj.name) - _p(2, 'CXX_STANDARD %s', standard[cfg.cppdialect]) - _p(2, 'CXX_STANDARD_REQUIRED YES') - _p(2, 'CXX_EXTENSIONS %s', extentions) - _p(2, 'POSITION_INDEPENDENT_CODE %s', pic) - _p(2, 'INTERPROCEDURAL_OPTIMIZATION %s', lto) - _p(1, ')') - end - - -- precompiled headers + if sanitize_addresss_options ~= "" then + _p(0, 'target_link_options("%s" PRIVATE %s)', prj.name, sanitize_addresss_options) + end + if sanitize_fuzzer_options ~= "" then + _p(0, 'target_link_options("%s" PRIVATE %s)', prj.name, sanitize_fuzzer_options) + end + + -- precompiled headers + local pch = generator_expression(prj, function(cfg) -- copied from gmake2_cpp.lua if not cfg.flags.NoPCH and cfg.pchheader then local pch = cfg.pchheader @@ -332,90 +431,113 @@ function m.generate(prj) if not found then pch = project.getrelative(cfg.project, path.getabsolute(pch)) end - - _p(1, 'target_precompile_headers("%s" PUBLIC "%s")', prj.name, pch) + return {pch} end + return {} + end, one_expression) + if pch ~= "" then + _p(0, 'target_precompile_headers("%s" PUBLIC %s)', prj.name, pch) + end - -- pre/post buildcommands + -- prebuild commands + local prebuildcommands = generator_expression(prj, function(cfg) + local res = {} if cfg.prebuildmessage or #cfg.prebuildcommands > 0 then - -- add_custom_command PRE_BUILD runs just before generating the target - -- so instead, use add_custom_target to run it before any rule (as obj) - _p(1, 'add_custom_target(prebuild-%s', prj.name) if cfg.prebuildmessage then - local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location) - _p(2, 'COMMAND %s', command) - end - local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location) - for _, command in ipairs(commands) do - _p(2, 'COMMAND %s', command) + table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location)) end - _p(1, ')') - _p(1, 'add_dependencies(%s prebuild-%s)', prj.name, prj.name) + res = table.join(res, os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)) end + return res + end, table_expression) + if #prebuildcommands > 0 then + -- add_custom_command PRE_BUILD runs just before generating the target + -- so instead, use add_custom_target to run it before any rule (as obj) + _p(0, 'add_custom_target(prebuild-%s', prj.name) + for _, command in ipairs(prebuildcommands) do + _p(1, 'COMMAND %s', command) + end + _p(0, ')') + _p(0, 'add_dependencies(%s prebuild-%s)', prj.name, prj.name) + end + -- prelink commands + local prelinkcommands = generator_expression(prj, function(cfg) + local res = {} if cfg.prelinkmessage or #cfg.prelinkcommands > 0 then - _p(1, 'add_custom_command(TARGET %s PRE_LINK', prj.name) if cfg.prelinkmessage then - local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prelinkmessage), cfg.project.basedir, cfg.project.location) - _p(2, 'COMMAND %s', command) + table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prelinkmessage), cfg.project.basedir, cfg.project.location)) end - local commands = os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location) - for _, command in ipairs(commands) do - _p(2, 'COMMAND %s', command) - end - _p(1, ')') + res = table.join(res, os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location)) + end + return res + end, table_expression) + if #prelinkcommands > 0 then + _p(0, 'add_custom_command(TARGET %s PRE_LINK', prj.name) + for _, command in ipairs(prelinkcommands) do + _p(1, 'COMMAND %s', command) end + _p(0, ')') + end + -- postbuild commands + local postbuildcommands = generator_expression(prj, function(cfg) + local res = {} if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then - _p(1, 'add_custom_command(TARGET %s POST_BUILD', prj.name) if cfg.postbuildmessage then - local command = os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location) - _p(2, 'COMMAND %s', command) - end - local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location) - for _, command in ipairs(commands) do - _p(2, 'COMMAND %s', command) + table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location)) end - _p(1, ')') + res = table.join(res, os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)) end - -- custom command - local function addCustomCommand(fileconfig, filename) - if #fileconfig.buildcommands == 0 or #fileconfig.buildoutputs == 0 then - return - end - - local custom_output_directories = table.unique(table.translate(fileconfig.buildoutputs, function(output) return project.getrelative(cfg.project, path.getdirectory(output)) end)) - -- Alternative would be to add 'COMMAND ${CMAKE_COMMAND} -E make_directory %s' to below add_custom_command - _p(1, 'file(MAKE_DIRECTORY %s)', table.implode(custom_output_directories, "", "", " ")) + return res + end, table_expression) + if #postbuildcommands > 0 then + _p(0, 'add_custom_command(TARGET %s PRE_LINK', prj.name) + for _, command in ipairs(postbuildcommands) do + _p(1, 'COMMAND %s', command) + end + _p(0, ')') + end - _p(1, 'add_custom_command(TARGET OUTPUT %s', table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) - if fileconfig.buildmessage then - _p(2, 'COMMAND %s', os.translateCommandsAndPaths('{ECHO} ' .. m.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) - end - for _, command in ipairs(fileconfig.buildcommands) do - _p(2, 'COMMAND %s', os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) - end - if filename ~= "" and #fileconfig.buildinputs ~= 0 then - filename = filename .. " " - end - if filename ~= "" or #fileconfig.buildinputs ~= 0 then - _p(2, 'DEPENDS %s', filename .. table.implode(fileconfig.buildinputs,"",""," ")) - end - _p(1, ')') - if not fileconfig.compilebuildoutputs then - local target_name = 'CUSTOM_TARGET_' .. filename:gsub('/', '_'):gsub('\\', '_') - _p(1, 'add_custom_target(%s DEPENDS %s)', target_name, table.implode(project.getrelative(cfg.project, fileconfig.buildoutputs),"",""," ")) - _p(1, 'add_dependencies(%s %s)', prj.name, target_name) - end + -- custom command +-- local custom_output_directories_by_cfg = {} + local custom_commands_by_filename = {} + + local function addCustomCommand(cfg, fileconfig, filename) + if #fileconfig.buildcommands == 0 or #fileconfig.buildoutputs == 0 then + return + end +--[[ + custom_output_directories_by_cfg[cfg] = custom_output_directories_by_cfg[cfg] or {} + custom_output_directories_by_cfg[cfg] = table.join(custom_output_directories_by_cfg[cfg], table.translate(fileconfig.buildoutputs, function(output) return project.getrelative(prj, path.getdirectory(output)) end)) +--]] + custom_commands_by_filename[filename] = custom_commands_by_filename[filename] or {} + custom_commands_by_filename[filename][cfg] = custom_commands_by_filename[filename][cfg] or {} + custom_commands_by_filename[filename][cfg]["outputs"] = project.getrelative(cfg.project, fileconfig.buildoutputs) + custom_commands_by_filename[filename][cfg]["commands"] = {} + custom_commands_by_filename[filename][cfg]["depends"] = {} + custom_commands_by_filename[filename][cfg]["compilebuildoutputs"] = fileconfig.compilebuildoutputs + + if fileconfig.buildmessage then + table.insert(custom_commands_by_filename[filename][cfg]["commands"], os.translateCommandsAndPaths('{ECHO} ' .. m.quote(fileconfig.buildmessage), cfg.project.basedir, cfg.project.location)) + end + for _, command in ipairs(fileconfig.buildcommands) do + table.insert(custom_commands_by_filename[filename][cfg]["commands"], os.translateCommandsAndPaths(command, cfg.project.basedir, cfg.project.location)) + end + if filename ~= "" then + table.insert(custom_commands_by_filename[filename][cfg]["depends"], filename) end - local tr = project.getsourcetree(cfg.project) - p.tree.traverse(tr, { - onleaf = function(node, depth) + custom_commands_by_filename[filename][cfg]["depends"] = table.join(custom_commands_by_filename[filename][cfg]["depends"], fileconfig.buildinputs) + end + local tr = project.getsourcetree(prj) + p.tree.traverse(tr, { + onleaf = function(node, depth) + for cfg in project.eachconfig(prj) do local filecfg = p.fileconfig.getconfig(node, cfg) local rule = p.global.getRuleForFile(node.name, prj.rules) if p.fileconfig.hasFileSettings(filecfg) then - addCustomCommand(filecfg, node.relpath) + addCustomCommand(cfg, filecfg, node.relpath) elseif rule then local environ = table.shallowcopy(filecfg.environ) @@ -424,14 +546,71 @@ function m.generate(prj) p.rule.prepareEnvironment(rule, environ, filecfg) end local rulecfg = p.context.extent(rule, environ) - addCustomCommand(rulecfg, node.relpath) + addCustomCommand(cfg, rulecfg, node.relpath) end end - }) - addCustomCommand(cfg, "") - _p('endif()') - _p('') + end + }) + +--[[ + local custom_output_directories = generator_expression(prj, function(cfg) return table.difference(table.unique(custom_output_directories_by_cfg[cfg]), {"."}) end, table_expression) + if not is_empty(custom_output_directories) then + -- Alternative would be to add 'COMMAND ${CMAKE_COMMAND} -E make_directory %s' to below add_custom_command + _p(0, '# Custom output directories') + _p(0, 'file(MAKE_DIRECTORY') + for _, dir in ipairs(custom_output_directories) do + _p(1, '%s', dir) + end + _p(0, ')') + end +--]] + for filename, custom_ouput_by_cfg in pairs(custom_commands_by_filename) do + --local custom_outputs_directories = generator_expression(prj, function(cfg) + -- return table.difference(table.unique(project.getrelative(prj, table.translate(custom_ouput_by_cfg[cfg]["outputs"], path.getdirectory))), {".", ""}) + -- end, table_expression) + local _, same_output_by_cfg = generator_expression(prj, function(cfg) return custom_ouput_by_cfg[cfg]["outputs"] end, table_expression) + --local custom_commands = generator_expression(prj, function(cfg) return custom_ouput_by_cfg[cfg]["commands"] end, table_expression) + --local depends = generator_expression(prj, function(cfg) return custom_ouput_by_cfg[cfg]["depends"] end, table_expression) + + for cfg in project.eachconfig(prj) do + _p(0, 'add_custom_command(TARGET OUTPUT %s', table.implode(custom_ouput_by_cfg[cfg]["outputs"], "", "", " ")) + custom_outputs_directories = table.difference(table.unique(project.getrelative(prj, table.translate(custom_ouput_by_cfg[cfg]["outputs"], path.getdirectory))), {".", ""}) + if not is_empty(custom_outputs_directories) then + _p(1, 'COMMAND ${CMAKE_COMMAND} -E make_directory %s', table.implode(custom_outputs_directories, "", "", " ")) + end + for _, command in ipairs(custom_ouput_by_cfg[cfg]["commands"]) do + _p(1, 'COMMAND %s', command) + end + for _, dep in ipairs(custom_ouput_by_cfg[cfg]["depends"]) do + _p(1, 'DEPENDS %s', dep) + end + + _p(0, ')') + if same_output_by_cfg then break end + end + + --local custom_target_by_cfg = {} + for cfg in project.eachconfig(prj) do + if not custom_ouput_by_cfg[cfg]["compilebuildoutputs"] then + local config_prefix = (same_output_by_cfg and "") or cfg.buildcfg .. '_' + local target_name = 'CUSTOM_TARGET_' .. config_prefix .. filename:gsub('/', '_'):gsub('\\', '_') + --custom_target_by_cfg[cfg] = target_name + _p(0, 'add_custom_target(%s DEPENDS %s)', target_name, table.implode(custom_ouput_by_cfg[cfg]["outputs"],"",""," ")) + + _p(0, 'add_dependencies(%s %s)', prj.name, target_name) + if same_output_by_cfg then break end + end + end + --[[ add_dependencies doesn't support generator expression :/ + local custom_dependencies = generator_expression(prj, function(cfg) return {custom_target_by_cfg[cfg]} end, table_expression) + _p(0, 'add_dependencies(%s', prj.name) + for _, target in ipairs(custom_dependencies) do + _p(1, '%s', target) + end + _p(0, ')') + --]] end + _p('') -- restore path.getDefaultSeparator = oldGetDefaultSeparator end From 1871a3caa5a4d4b9561d15b02dc23c3e4c6a26bb Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Mon, 21 Oct 2024 10:41:31 +0200 Subject: [PATCH 26/27] Handle invalid char in configuration/platform name (as hyphen) (`$` produces errors) --- cmake.lua | 15 +++++++++++---- cmake_project.lua | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/cmake.lua b/cmake.lua index 0d6e3de..347b8bc 100644 --- a/cmake.lua +++ b/cmake.lua @@ -36,13 +36,20 @@ function cmake.generateProject(prj) end end +local function normalize_identifier(name) + local res = string.gsub(name, "[^a-zA-Z0-9_]", "_") + if res ~= name then + premake.warnOnce("cmake_identifier_" .. name, 'configuration "' .. name .. '" contains unsuported characters, replaced by "' .. res .. '"') + end + return res +end + function cmake.cfgname(cfg) - local cfgname = cfg.buildcfg if cmake.workspace.multiplePlatforms then - -- CMake breaks if "|" is used here - cfgname = string.format("%s-%s", cfg.platform, cfg.buildcfg) + return string.format("%s_%s", normalize_identifier(cfg.platform), normalize_identifier(cfg.buildcfg)) + else + return normalize_identifier(cfg.buildcfg) end - return cfgname end function cmake.cleanWorkspace(wks) diff --git a/cmake_project.lua b/cmake_project.lua index 736e4c7..22c2362 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -592,7 +592,7 @@ function m.generate(prj) --local custom_target_by_cfg = {} for cfg in project.eachconfig(prj) do if not custom_ouput_by_cfg[cfg]["compilebuildoutputs"] then - local config_prefix = (same_output_by_cfg and "") or cfg.buildcfg .. '_' + local config_prefix = (same_output_by_cfg and "") or cmake.cfgname(cfg) .. '_' local target_name = 'CUSTOM_TARGET_' .. config_prefix .. filename:gsub('/', '_'):gsub('\\', '_') --custom_target_by_cfg[cfg] = target_name _p(0, 'add_custom_target(%s DEPENDS %s)', target_name, table.implode(custom_ouput_by_cfg[cfg]["outputs"],"",""," ")) From 8e02bb91a4d0f29d7540de7357574cf3b7c454f9 Mon Sep 17 00:00:00 2001 From: Jarod42 Date: Tue, 22 Oct 2024 11:52:30 +0200 Subject: [PATCH 27/27] Fix pre/post build commands by config. --- cmake_project.lua | 163 +++++++++++++++++++++++++++++++--------------- 1 file changed, 109 insertions(+), 54 deletions(-) diff --git a/cmake_project.lua b/cmake_project.lua index 22c2362..6b4cf53 100755 --- a/cmake_project.lua +++ b/cmake_project.lua @@ -149,6 +149,112 @@ local function generator_expression(prj, callback, mode) end end +local function generate_prebuild(prj) + local prebuildcommands, same_output_by_cfg = generator_expression(prj, function(cfg) + local res = {} + if cfg.prebuildmessage or #cfg.prebuildcommands > 0 then + if cfg.prebuildmessage then + table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location)) + end + res = table.join(res, os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)) + end + return res + end, table_expression) + if #prebuildcommands == 0 then + return + end + local commands = {} + if not same_output_by_cfg then + for i, command in ipairs(prebuildcommands) do + local variable_name = string.format("PREBUILD_COMMAND_%s_%i", prj.name, i) + _p(0, 'SET(%s %s)', variable_name, command) + commands[i] = '"${' .. variable_name .. '}"' + end + else + commands = prebuildcommands + end + -- add_custom_command PRE_BUILD runs just before generating the target + -- so instead, use add_custom_target to run it before any rule (as obj) + _p(0, 'add_custom_target(prebuild-%s', prj.name) + for _, command in ipairs(commands) do + _p(1, 'COMMAND %s', command) + end + if not same_output_by_cfg then + _p(1, 'COMMAND_EXPAND_LISTS') + end + _p(0, ')') + _p(0, 'add_dependencies(%s prebuild-%s)', prj.name, prj.name) +end + +local function generate_prelink(prj) + local prelinkcommands, same_output_by_cfg = generator_expression(prj, function(cfg) + local res = {} + if cfg.prelinkmessage or #cfg.prelinkcommands > 0 then + if cfg.prelinkmessage then + table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prelinkmessage), cfg.project.basedir, cfg.project.location)) + end + res = table.join(res, os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location)) + end + return res + end, table_expression) + if #prelinkcommands == 0 then + return + end + local commands = {} + if not same_output_by_cfg then + for i, command in ipairs(prelinkcommands) do + local variable_name = string.format("PRELINK_COMMAND_%s_%i", prj.name, i) + _p(0, 'SET(%s %s)', variable_name, command) + commands[i] = '"${' .. variable_name .. '}"' + end + else + commands = prelinkcommands + end + _p(0, 'add_custom_command(TARGET %s PRE_LINK', prj.name) + for _, command in ipairs(commands) do + _p(1, 'COMMAND %s', command) + end + if not same_output_by_cfg then + _p(1, 'COMMAND_EXPAND_LISTS') + end + _p(0, ')') +end + +local function generate_postbuild(prj) + local postbuildcommands, same_output_by_cfg = generator_expression(prj, function(cfg) + local res = {} + if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then + if cfg.postbuildmessage then + table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location)) + end + res = table.join(res, os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)) + end + return res + end, table_expression) + if #postbuildcommands == 0 then + return + end + local commands = {} + if not same_output_by_cfg then + for i, command in ipairs(postbuildcommands) do + local variable_name = string.format("POSTBUILD_COMMAND_%i_%s", i, prj.name) + _p(0, 'SET(%s %s)', variable_name, command) + commands[i] = '"${' .. variable_name .. '}"' + end + else + commands = postbuildcommands + end + + _p(0, 'add_custom_command(TARGET %s POST_BUILD', prj.name) + for _, command in ipairs(commands) do + _p(1, 'COMMAND %s', command) + end + if not same_output_by_cfg then + _p(1, 'COMMAND_EXPAND_LISTS') + end + _p(0, ')') +end + -- -- Project: Generate the cmake project file. -- @@ -440,64 +546,13 @@ function m.generate(prj) end -- prebuild commands - local prebuildcommands = generator_expression(prj, function(cfg) - local res = {} - if cfg.prebuildmessage or #cfg.prebuildcommands > 0 then - if cfg.prebuildmessage then - table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prebuildmessage), cfg.project.basedir, cfg.project.location)) - end - res = table.join(res, os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)) - end - return res - end, table_expression) - if #prebuildcommands > 0 then - -- add_custom_command PRE_BUILD runs just before generating the target - -- so instead, use add_custom_target to run it before any rule (as obj) - _p(0, 'add_custom_target(prebuild-%s', prj.name) - for _, command in ipairs(prebuildcommands) do - _p(1, 'COMMAND %s', command) - end - _p(0, ')') - _p(0, 'add_dependencies(%s prebuild-%s)', prj.name, prj.name) - end + generate_prebuild(prj) -- prelink commands - local prelinkcommands = generator_expression(prj, function(cfg) - local res = {} - if cfg.prelinkmessage or #cfg.prelinkcommands > 0 then - if cfg.prelinkmessage then - table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.prelinkmessage), cfg.project.basedir, cfg.project.location)) - end - res = table.join(res, os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location)) - end - return res - end, table_expression) - if #prelinkcommands > 0 then - _p(0, 'add_custom_command(TARGET %s PRE_LINK', prj.name) - for _, command in ipairs(prelinkcommands) do - _p(1, 'COMMAND %s', command) - end - _p(0, ')') - end + generate_prelink(prj) -- postbuild commands - local postbuildcommands = generator_expression(prj, function(cfg) - local res = {} - if cfg.postbuildmessage or #cfg.postbuildcommands > 0 then - if cfg.postbuildmessage then - table.insert(res, os.translateCommandsAndPaths("{ECHO} " .. m.quote(cfg.postbuildmessage), cfg.project.basedir, cfg.project.location)) - end - res = table.join(res, os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)) - end - return res - end, table_expression) - if #postbuildcommands > 0 then - _p(0, 'add_custom_command(TARGET %s PRE_LINK', prj.name) - for _, command in ipairs(postbuildcommands) do - _p(1, 'COMMAND %s', command) - end - _p(0, ')') - end + generate_postbuild(prj) -- custom command -- local custom_output_directories_by_cfg = {}