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

Reply via email to