The Premake discussion forums here at Industrious One are now READ ONLY.

For questions about using Premake and authoring project scripts, please ask on StackOverflow, adding the #premake tag to your question. More people (including the Premake developers) will be able to see and respond to your questions, and you will have the opportunity to reward the best answers.

For discussions about developing, customizing, or extending Premake and add-on modules, please use the new Premake Developers Google Groups forum.

 


 

CMake integration

The great thing about CMake is a great number of FindXXX scripts allowing to use different 3rd party software in your projects easily. This scripts output their results in form of several variables with documented names, containing location of package's include file, libraries, etc.

The next script allows you to query values of variables from CMake scripts. It requires CMake to be installed.

--
-- cmake.lua
-- Run CMake scripts and get variables from them
-- Copyright (c) 2011 Konstantin Tokarev 
--
 
cmake = {}
 
local cmake_code = ""
 
local function cmake_requestcode()
    return [[
if(NOT PREMAKE_REQUEST_VARS)
    message(FATAL_ERROR "Error in CMake integration: PREMAKE_REQUEST_VARS not defined")
endif()
foreach(var ${PREMAKE_REQUEST_VARS})
    message(${var}="${${var}}")
endforeach()
]]
end
 
--
-- Full path to CMake executable
--
cmake.command = "cmake"
 
--
-- Add piece of CMake code into listing
-- 
-- @param code
--     CMake code
--
function cmake.addcode(code)
    cmake_code = cmake_code.."\n"..code
end
 
--
-- Include CMake file or module.
-- See "include" command in CMake documentation for more details.
--
-- @param mod
--     Name of CMake file or module
--
function cmake.include(mod)
    cmake.addcode("include("..mod..")")
end
 
--
-- Set up path to local CMake modules.
-- See description of "CMAKE_MODULE_PATH" variable in CMake
-- documentation for mode details
--
-- @param path
--     List of directories (separated with ";") to search for
--     CMake modules, use by "include" and "find_package"
--
function cmake.module_path(path)
    cmake.addcode("set(CMAKE_MODULE_PATH "..path..")")
end
 
--
-- The same as CMake "find_package" command
--
-- Add "REQUIRED" after package name to raise fatal error
-- if package not found
--
--  @param pkg
--      Package name optionally followed by REQUIRED or other
--      modifiers (see CMake documentation on "find_package"
--      command)
--
function cmake.find_package(pkg)
    cmake.addcode("find_package("..pkg..")")
end
 
--
-- Set variable in CMake listing
--
-- @param var
--     CMake variable name
-- @param value
--     CMake variable value
--
function cmake.set(var, value)
    cmake.addcode("set("..var.." "..value..")")
end
 
--
-- Execute current CMake listing and return values of requested
-- variables.
--
-- This function will produce an error if CMake execution fails.
--
-- @param variables
--     List of CMake variables
-- @return
--     Table containing values of requested variables using variable
--     names as keys
--
function cmake.getvariables(variables)
    cmake.addcode(cmake_requestcode())
    local tmpname = os.tmpname()
    local tmpfile = io.open(tmpname, "w")
    tmpfile:write(cmake_code)
    tmpfile:close()
    table.insert(variables, "_CMAKE_EXITCODE")
    local cmd = cmake.command..
        " -DPREMAKE_REQUEST_VARS=\""..
        table.concat(variables, ";")..
        "\" -P "..tmpname.." 2>&1"
    if os.get() ~= "windows" then
        cmd = cmd.."; echo _CMAKE_EXITCODE=\\\"$?\\\""
    else
        cmd = cmd.."& set errorlevel= & echo \"%errorlevel%\""
    end
    local output = os.outputof(cmd)
    os.remove(tmpname)
    local lines = string.explode(output, "\n")
    local values = {}
    for i,v in ipairs(lines) do
        for j = 1,table.getn(variables) do
            if string.startswith(v, variables[j].."=\"") then
                local val = v:match("=\"(.+)\"")
                values[variables[j]] = val
            end
        end
    end
    if tonumber(values["_CMAKE_EXITCODE"]) ~= 0 then
        error("CMake execution failed:\n\n"..output, 2)
    end
    return values
end
 
--
-- Discard current CMake listing
--
function cmake.reset()
    cmake_code = ""
end

Example of usage:

cmake.find_package("Gettext REQUIRED")
 
-- Here execute CMake and retrieve variables we want to use from it's output
local var = cmake.getvariables{"GETTEXT_MSGMERGE_EXECUTABLE", "GETTEXT_MSGFMT_EXECUTABLE", "GETTEXT_FOUND"}
 
if var["GETTEXT_FOUND"] == "TRUE" then
    print(var["GETTEXT_MSGMERGE_EXECUTABLE"].."\n"..var["GETTEXT_MSGFMT_EXECUTABLE"])
end
 
-- Prepare to new CMake run
cmake.reset()
 
cmake.set("GETTEXT_MSGMERGE_EXECUTABLE", "abcd")
local v = cmake.getvariables{"GETTEXT_MSGMERGE_EXECUTABLE"}
print(v["GETTEXT_MSGMERGE_EXECUTABLE"]) -- Prints "abcd"

Hi,

Let's assume that I have a project A that requires a library B. I want to use Premake to define A but B is already defined by it's developers with CMake.

If my understanding is correct, with the functions in this script, I should be able to call the CMake generation of B, and retrieve the resulting paths (include, lib, etc.) that would be used in my project A definition.

Am I correct or is this script less than that?

Thanks.

Unfortunately, it's not possible to call scripts that build anything - they won't run in "script mode" (cmake -P). However, you should be able to load typical find modules and other helper scripts.

BTW, some libraries using CMake install so-called "config" files with .cmake extension, containing installation versions and paths. You should be able to use them (pkg-config may be more straightforward, though)

That's not good enough for me actually : I have all my dependencies depending in CMake. It seems impossible to sell Premake where are the generated project files of CMake in a generic and cross-platform way without Premake getting these informations from CMake.

Looks like working with two build systems because of dependencies you want sources of is just hell. :(

You can just build all your dependencies (e.g., with os.execute()), install inside your project build directory, and then link to them (you know all paths because you've set install path before).

This actions could be factored into reusable script; feel free to do it if you want.

Just discovered that newest CMake versions 2.8.6 and 2.8.7 provide "--find-package" mode, which may work better for this purpose than "-P" mode (e.g. because it allows commands like add_library() inside loaded CMake code)