Hi all,
I've been working on a generic cmake stub that allows libraries to be
built with multiple configuration options a.k.a. multilib support in the
style of GCC, on behalf of my employer. This feature allows a user to
build multiple versions of the same library from the same source base
but compiled with different options and ABIs. Automake provides a
similar feature via macros in multilib.am.
May not seem very useful for desktops, but its pertinent for
embedded/cross development. Typical ARM/MIPS toolchains supply a dozen
multilib configurations, at least. I'd restrict the module to work with
GNU CC, but it seems to play along just fine with LLVM Clang compiler,
which has a gcc-compatible command-line.
Please review the attached module. Pasting an example in-line to
hopefully pique someone's interest.
Regards,
Faraz Shahbazker
Imagination Technologies
###########
# Example Usage
###########
set(sources_math sin.c cos.s floor.c ...)
include(MultiLibConfig)
# Initializer
multilib_build_setup()
if (MULTILIB_TOP)
<Do everything you'd want to do only once, like installing headers,
top-level checks, etcetra>
else () # MULTILIB_TOP
## This block is automagically repeated for each multilib configuration
# Creates target, sets compiler flags & a handle for the user to
override target properties later.
# Signature is same as add_library()
multilib_add_library(m STATIC ${sources_math})
# Use the target handle `MULTILIB_TARGET_m' for customization
set_target_properties(${MULTILIB_TARGET_m} PROPERTIES OUTPUT_NAME mymath)
# Install, once again using the current target handle
install(TARGETS ${MULTILIB_TARGET_m} ARCHIVE DESTINATION
lib/${MULTILIB_DIR})
endif () # MULTILIB_TOP
## Configure with
## cmake -DMULTILIB_SELECT="ALL" <srcdir>
#.rst:
# MultiLibConfig
# --------------
#
# This module provides generic stubs for building libraries with a C
# compiler that supports multi-lib configurations with the
# command-line option '-print-multi-lib'. Output format is expected to
# match that of . For compilers that do not support this option,
# MultiLibConfig has no effect and the default configuration of the
# library is built.
#
# Fine grained control of multilib configurations is provided with the
# variables MULTILIB_SELECT and MULTILIB_SKIP.
#
# MULTILIB_SELECT=[ALL|<regex>]
# ALL: all configurations, except those skipped by MULTILIB_SKIP are
# built
# <regex>: only configs matching the pattern, excluding those skipped by
# MULTILIB_SKIP are built
# If MULTILIB_SELECT is not specified, only the default configuration
# is built.
#
# MULTILIB_SKIP=[NONE|<regex>]
# NONE: all configurations selected by MULTILIB_SELECT are built
# regex: only those configurations selected by MULTILIB_SELECT, which
# do NOT match the pattern are built
# If MULTILIB_SKIP is not specified, behaviour is same as
# MULTILIB_SKIP=NONE. If MULTILIB_SELECT is not specified, MULTILIB_SKIP
# is ignored.
#
# This module defines the following variables:
#
# .. variable:: MULTILIB_TOP
# Flag to indicate top-level of multilib recursion, after creating
# multilib targets.
#
# .. variable:: MULTILIB_FLAVOUR
# `Compiler options' part of a multilib descriptor, only visible in
# the recursive sub-context.
#
# .. variable:: MULTILIB_DIR
# `Target directory' part of multilib descriptor, only visible in the
# recursive sub-context.
#
# .. variable:: MULTILIB_NAME
# Same as MULTILIB_FLAVOUR, or 'default' for the default multilib
# configuration, where MULTILIB_FLAVOUR is empty. Only visible in the
# recursive sub-context.
#
# .. variable:: MULTILIB_TARGET_<name>
# For each library added, a reference to the current multilib target.
#
# Example Usage:
# .. code-block::
set(sources_math sin.c cos.s floor.c ...)
include(MultiLibConfig)
multilib_build_setup()
if (MULTILIB_TOP)
<Do everything you want to do only once, like installing headers,
top-level checks, etcetra>
else (MULTILIB_TOP)
## build the library for each multilib configuration>
## This step creates the target and sets up compiler flags for it based
## on the multilib configuration. It also creates a handle for the user
## to override target properties later.
add_library_multilib(m STATIC ${sources_math})
## The handle MULTILIB_TARGET_<name> allows user to override target
## properties or set install path
set_target_properties(${MULTILIB_TARGET_m} PROPERTIES OUTPUT_NAME mymath)
install(TARGETS ${MULTILIB_TARGET_m}
ARCHIVE DESTINATION lib/${MULTILIB_DIR})
endif (MULTILIB_TOP)
#
#=============================================================================
# Copyright 2014 Imagination Technologies Limited
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
cmake_minimum_required(VERSION 2.6)
#.rst:
# .. command:: multilib_recurse_step
#
# Do a recursive CMake invocation for the current project with a new output
# directory name determined by the mutilib _flavour string. If no
# MULTILIB_SELECT parameter is provided, this can be used to flash-build
# current project with a custom set of CFLAGS (separated by '@').
#
# multilib_recurse_step()
#
function (multilib_recurse_step _FLAVOUR _DIR )
set(MULTILIB_DIR "${_DIR}")
# make doesn't like = within target names, replace with ~
string(REPLACE "=" "~" MULTILIB_FLAVOUR "${_FLAVOUR}")
if ("${MULTILIB_FLAVOUR}" STREQUAL "")
# Specify a non-empty build directory for default configuration
set(MULTILIB_NAME "default")
else () # MULTILIB_FLAVOUR != ""
# else derive build-directory from configuration string
set(MULTILIB_NAME ${MULTILIB_FLAVOUR})
endif() # ${MULTILIB_FLAVOUR} == ""
# Recurse to build this multilib configuration
add_subdirectory(. "${MULTILIB_NAME}")
endfunction()
#.rst:
# .. command:: multilib_build_setup
#
# Recursive magic for building multi-lib targets. This sets the flag
# MULTILIB_TOP to indicate the top-level invocation. For sub-invocations, it
# calls multilib_recurse_step.
#
# multilib_build_setup()
#
macro (multilib_build_setup)
message(status "select ${MULTILIB_SELECT}")
if ((DEFINED MULTILIB_SELECT) AND NOT (DEFINED MULTILIB_FLAVOUR))
execute_process(COMMAND ${CMAKE_C_COMPILER} "-print-multi-lib"
OUTPUT_VARIABLE _C_MULTILIBS)
# Default setup for compilers that don't support -print-multi-lib
if (NOT _C_MULTILIBS)
set(_C_MULTILIBS ".;")
endif () # _C_MULTILIBS
message(status "LIBS ${_C_MULTILIBS}")
# Mangle multi-lib output so we can tokenize easily with foreach loop
string(REGEX REPLACE ";" "\\\\;" _C_MULTILIBS "${_C_MULTILIBS}")
string(REGEX REPLACE "\n" ";" _C_MULTILIBS "${_C_MULTILIBS}")
if (MULTILIB_SELECT AND (NOT "${MULTILIB_SELECT}" STREQUAL "ALL"))
# To filter the multilib configurations that we want to build
set(_SELECT_REGEX ${MULTILIB_SELECT})
# make doesn't like = within target names, replace with ~
string(REPLACE "=" "~" MULTILIB_SELECT "${MULTILIB_SELECT}")
else () # (${MULTILIB_SELECT} == ALL)
# Match everything so that all configurations get built
set(_SELECT_REGEX ".*")
endif () # MULTILIB_SELECT && (${MULTILIB_SELECT} != ALL)
if (MULTILIB_SKIP AND (NOT "${MULTILIB_SKIP}" STREQUAL "NONE"))
# To filter the multilib configurations that we don't want to build
set(_SKIP_REGEX ${MULTILIB_SKIP})
else () # ${MULTILIB_SKIP} != NONE
# Match nothing so that no configurations are skipped over
set(_SKIP_REGEX "^$")
endif () # MULTILIB_SKIP && ${MULTILIB_SKIP} != NONE
foreach (mlib_config ${_C_MULTILIBS})
string(REGEX MATCH "@.*" _FLAVOUR "${mlib_config}")
string(REGEX MATCH "[^;]*" _DIR "${mlib_config}")
if (_FLAVOUR)
string(REGEX MATCH "${_SELECT_REGEX}" _SELECT_MATCH "${_FLAVOUR}")
string(REGEX MATCH "${_SKIP_REGEX}" _SKIP_MATCH "${_FLAVOUR}")
endif() # _FLAVOUR
if (_SELECT_MATCH AND NOT _SKIP_MATCH)
multilib_recurse_step(${_FLAVOUR} ${_DIR})
endif () # MATCH && !SKIP
endforeach () # mlib_config
# Set a flag to indicate top-level invocation of CMake
set(MULTILIB_TOP YES)
# Clean-up intermediates
unset(_SELECT_REGEX)
unset(_SKIP_REGEX)
unset(_C_MULTILIBS)
else () # !MULTILIB_SELECT || MULTILIB_FLAVOUR
# The vanilla 'run'-mode to build actual targets
# Break in to individual multi-lib compiler options
string(REPLACE "@" " -" MULTILIB_CFLAGS "${MULTILIB_FLAVOUR}")
# Revert ~ back to = for the compiler command-line
string(REPLACE "~" "=" MULTILIB_CFLAGS "${MULTILIB_CFLAGS}")
# Unset flag to indicate leaf-level invocation of cmake
set(MULTILIB_TOP NO)
endif () # MULTILIB_SELECT && !MULTILIB_FLAVOUR
endmacro ()
#.rst:
# .. command:: add_library_multilib
#
# Create a library target for current multilib configuration and generate a
# variable MULTILIB_TARGET_<name>, based on the first input parameter to
# provide a handle to the new target.
#
# add_library_multilib(_NAME ... ARGN)
#
macro (add_library_multilib _name)
# Add target with the name embellished with multilib config string
add_library (${MULTILIB_FLAVOUR}${_name} ${ARGN})
set_target_properties (${MULTILIB_FLAVOUR}${_name} PROPERTIES
# Reset library output-name to user-specified name
OUTPUT_NAME ${_name}
# Add multi-lib build options to compiler command-line for this target
COMPILE_FLAGS "${MULTILIB_CFLAGS}")
# Set up a handle for user to access/modify properties for this target
set(MULTILIB_TARGET_${_name} ${MULTILIB_FLAVOUR}${_name})
endmacro ()
#
# End of multilib.cmake
#
--
Powered by www.kitware.com
Please keep messages on-topic and check the CMake FAQ at:
http://www.cmake.org/Wiki/CMake_FAQ
Kitware offers various services to support the CMake community. For more
information on each offering, please visit:
CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html
Visit other Kitware open-source projects at
http://www.kitware.com/opensource/opensource.html
Follow this link to subscribe/unsubscribe:
http://public.kitware.com/cgi-bin/mailman/listinfo/cmake-developers