On 10/09/2011 02:09 AM, michael lowell roberts wrote:
> On Sat, Oct 8, 2011 at 6:40 AM, Michael Hertling <mhertl...@online.de>wrote:
> 
>> On 10/08/2011 05:33 AM, michael lowell roberts wrote:
>>> Hi all,
>>>
>>> I'm attempting to use the ExternalProject module to compile and link
>> against
>>> a library and I've run into a problem that I'm not certain how to
>> address.
>>> I'm using CMake 2.8.6 and Visual Studio 10 Express.
>>>
>>> The external project, in my case, is a CMake-compiled project and CMake
>>> predictably builds a matching configuration in the external project.
>>>
>>> The target_link_libraries() command, however, only allows for two
>>> configurations, at most, mapped to labels "debug" and "optimized."
>>>
>>> This allows for only two of the four default build configurations to
>>> complete successfully.
>>>
>>> For example, in my build scripts, I have told target_link_libraries() to
>> map
>>> "debug" configurations to the Debug version of library and optimized
>>> configurations map to the "RelWithDebInfo" build of the library.
>> Therefore,
>>> if I build the "Release" configuration, I get linker errors because the
>>> external project was also built with the "Release" configuration but I
>> need
>>> to link against "RelWithDebInfo."
>>>
>>> It seems to be that no matter how the target_link_library() mappings are
>> set
>>> up, only half of the default configurations will build properly because
>> of
>>> the dualism that target_link_libraries() uses.
>>>
>>> How do I get all four default build configurations working?
>>>
>>> Regards,
>>> Mike
>>
>> Could you boil down this issue to a minimal but self-contained
>> exemplary project and post it here for further investigation?
>>
> 
> Certainly.
> 
> I've attached a sample project that downloads and compiles the trio library,
> which can be found at the following site:
> 
> http://daniel.haxx.se/projects/trio/
> 
> It then attempts to link the library into a trivial executable that simply
> prints a message to the screen.
> 
> The build exhibits the problem I mentioned. Debug builds complete
> successfully. Release builds do not.

Okay, I was able to reproduce the issue. In fact, it is caused by your
manual mapping of configurations between your project and the external
project via TARGET_LINK_LIBRARIES()' debug/optimized keywords. However,
that's inappropriate anyways; you should use an imported library along
with its IMPORTED_LOCATION_<CONFIG> target properties instead. Look at
the attached trio.cmake file or the related diff, respectively. With
the new trio.cmake, I was able to build all configurations of the
exemplary project flawlessly, but please consider this just as a
hotfix, not a rock-solid solution.

> Additionally, the IDE appears incapable of determining when the build is
> up-to-date. Try to debug the executable to reproduce this problem. I'm
> inclined to believe that this is due to something I've done wrong and any
> help with this problem would also be appreciated.

I can't confirm this. When the test.c file ist touched, Visual Studio
recompiles it and relinkes the final executable as it should, and if
nothing has changed, nothing does happen. Could you provide further
information when the IDE does not behave as expected?

> Lastly, I'm certain that there are other problems I have not identified yet.
> If you could point out anything else that is obviously problematic, I would
> be grateful. Perhaps I am over-complicating the code, for example.

Hhm, yes, this might be the case... ;) Besides, there was a minor flaw
with include_directories(), see the diff, and constructions like

get_filename_component(TRIO_PREFIX "${TRIO_LIBRARY}" PATH)

doesn't seem to work well if TRIO_LIBRARY is something like

debug;.../lib/Debug/trio.lib;optimized;.../lib/RelWithDebInfo/trio.lib

AFAICS, the basic problem is that you need the trio library already
installed when your actual project is configured, and this can't be
done by a simple external project because the latter is completely
processed at build time, i.e. when your project's configuration is
already finished. For that reason, you need lines like

if(WIN32)
        set(TRIO_LIBRARY_NAME trio.lib)
else()
        set(TRIO_LIBRARY_NAME libtrio.a)
endif()

which is usually performed by CMake's FIND_LIBRARY() automatically.

IMO, possible solutions are:

- Integrate the external project's code base in your project's one,
  set it up appropriately and process it with an ADD_SUBDIRECTORY().
  However, in this way, you'll miss the external project's upstream.
- Apply the so-called super-build approach, i.e. provide a top-level
  CMakeLists.txt that pulls in the foreign project *and* your actual
  project as external projects with your project being the last one.
  In this manner, the foreign project is already build and possibly
  installed when your project is configured, so you can use CMake's
  FIND_*() commands to locate the foreign project's files. However,
  you must explicitly forward each variable that should be used in
  your project from the top-level CMakeLists.txt to your project's
  ExternalProject_Add() invocation, and there might also be issues
  w.r.t. the projects' common installation, see [1].

'hope that helps.

Regards,

Michael

[1] http://www.mail-archive.com/cmake@cmake.org/msg36170.html
# This file is part of the *ramalloc* project at <http://fmrl.org>.
# Copyright (c) 2011, Michael Lowell Roberts.
# All rights reserved. 
#
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions are 
# met: 
#
#  * Redistributions of source code must retain the above copyright 
#  notice, this list of conditions and the following disclaimer. 
#
#  * Redistributions in binary form must reproduce the above copyright 
#  notice, this list of conditions and the following disclaimer in the 
#  documentation and/or other materials provided with the distribution.
# 
#  * Neither the name of the copyright holder nor the names of 
#  contributors may be used to endorse or promote products derived 
#  from this software without specific prior written permission. 
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

# TODO: the CMake documentation for ExternalProjects says that the build
# steps have to be split up to be compatible with submitting to the 
# dashboard.

include(ExternalProject)

option(TRIO_DOWNLOAD 
        "indicates that i should download and build trio, if i can't find it."
        NO)
option(TRIO_RUN_TESTS 
        "indicates whether i should run tests on trio after building." 
        YES)
        
set(TRIO_DOWNLOAD_URL 
"http://ncu.dl.sourceforge.net/project/ctrio/trio/1.14/trio-1.14.tar.gz"; 
        CACHE STRING 
        "a URL describing where trio can be fetched.")
set(TRIO_MD5SUM 0278513e365675ca62bacb6f257b5045
        CACHE STRING
        "the MD5 sum of the file at TRIO_DOWNLOAD_URL.")

set(TRIO_LIBRARY_DOC "path(s) to the trio library.")
set(TRIO_DEFAULT_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/trio-prefix)

if(WIN32)
        set(TRIO_LIBRARY_NAME trio.lib)
else()
        set(TRIO_LIBRARY_NAME libtrio.a)
endif()

if(DEFINED CMAKE_CONFIGURATION_TYPES)
        # there's no official binary distribution for Windows. given the need to
        # support multi-configuration builds and without any convention for 
        # discovering where libraries for each configuration can be found, i'm
        # not going to try to find the library. feel free to set TRIO_LIBRARY
        # if you know where it is.

        # i can't make the following message an error that stops configuration.
        # if i did, it would prevent other options from being added to the
        # cache and would force the user to hit the configuration button more
        # than is really necessary.
        if(NOT TRIO_LIBRARY AND NOT TRIO_DOWNLOAD)
                message(WARNING "i don't know where trio is on your system. 
please put the path to trio.lib in TRIO_LIBRARY. or, if you prefer, you can set 
TRIO_DOWNLOAD to 1 if you would like me to download and build it for you.")
        endif()
else()
        find_library(TRIO_LIBRARY ${TRIO_LIBRARY_NAME} DOC ${TRIO_LIBRARY_DOC})
        # i can't make the following message an error that stops configuration.
        # if i did, it would prevent other options from being added to the
        # cache and would force the user to hit the configuration button more
        # than is really necessary.
        if(NOT TRIO_LIBRARY AND NOT TRIO_DOWNLOAD)
                message(WARNING "i couldn't find trio on your system. please 
put the path to libtrio.a in TRIO_LIBRARY. or, if you prefer, you can set 
TRIO_DOWNLOAD to 1 if you would like me to download and build it for you.")
        endif()
endif()
        
if(NOT TRIO_IS_A_TARGET AND TRIO_DOWNLOAD)
        if(UNIX)
                ExternalProject_Add(trio
                        PREFIX ${TRIO_DEFAULT_PREFIX}
                        URL ${TRIO_DOWNLOAD_URL}
                        URL_MD5 ${TRIO_MD5SUM}
                        CONFIGURE_COMMAND 
${TRIO_DEFAULT_PREFIX}/src/trio/configure --prefix=${TRIO_DEFAULT_PREFIX}
                        TEST_BEFORE_INSTALL ${TRIO_RUN_TESTS}   
                        )
        elseif(WIN32)
                # trio doesn't provide any capability to build on Windows 
                # out-of-the-box, so i use the patch stage to add a 
CMakeLists.txt
                # file into the project before building.
                # TODO: there's a bug in CMake <=3.8.5 that prevents 
ExternalProject_Add()
                # from installing trio into ${TRIO_DEFAULT_PREFIX}. i need to 
add
                # a version check here once i know which release fixes it.
                file(TO_NATIVE_PATH 
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/trio/CMakeLists.txt" src)
                file(TO_NATIVE_PATH "${TRIO_DEFAULT_PREFIX}/src/trio/" dest)
                ExternalProject_Add(trio
                        PREFIX ${TRIO_DEFAULT_PREFIX}
                        URL ${TRIO_DOWNLOAD_URL}
                        URL_MD5 ${TRIO_MD5SUM}
                        # BUG: trio's regression tests appear to fail on 
Windows. it's
                        # related to a specifier that i don't use (%m), so i'm 
not going
                        # worry too much about it. when i'm aware that a fix 
has been 
                        # released, i'll reenable tests.
                        #TEST_BEFORE_INSTALL ${TRIO_RUN_TESTS}
                        # the PREFIX keyword argument doesn't propigate to the 
external
                        # project's CMake configuration, so i specify it here 
explicitly.
                        # see <http://public.kitware.com/Bug/view.php?id=12357>.
                        CMAKE_ARGS 
-DCMAKE_INSTALL_PREFIX:PATH=${TRIO_DEFAULT_PREFIX}
                        # WORKAROUND: the following option needs to be last, as 
CMake
                        # appears to confuse arguments that follow for part of 
the
                        # command.
                        PATCH_COMMAND copy "${src}" "${dest}"
                        )
        else()
                message(FATAL_ERROR "i don't know how to compile an external 
project on this platform.")
        endif()
        ADD_LIBRARY(libtrio STATIC IMPORTED)
        if(DEFINED CMAKE_CONFIGURATION_TYPES)
                foreach(i ${CMAKE_CONFIGURATION_TYPES})
                        # WORKAROUND: target_link_libraries() doesn't recognize 
configuration
                        # names. instead, it recognizes keywords which cover 
categories
                        # of configurations. i elected to use Debug for *debug* 
and
                        # RelWithDebugInfo for everything else. unfortunately, 
this is 
                        # going to cause ExternalProject.cmake to compile 
unnecessary
                        # versions of trio for other configurations.
                        # if(i STREQUAL Debug)
                        #       list(APPEND trio_libs debug)
                        #       list(APPEND trio_libs 
"${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}")
                        # elseif(i STREQUAL RelWithDebInfo)
                        #       list(APPEND trio_libs optimized)
                        #       list(APPEND trio_libs 
"${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}")
                        # endif()
                        STRING(TOUPPER "${i}" j)
                        SET_TARGET_PROPERTIES(
                            libtrio
                            PROPERTIES
                            IMPORTED_LOCATION_${j} 
"${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}"
                        )
                endforeach()
                LIST(APPEND trio_libs libtrio)
                set(TRIO_LIBRARY 
                        ${trio_libs} 
                        CACHE STRING ${TRIO_LIBRARY_DOC} FORCE
                        )
        else()
                set(TRIO_LIBRARY 
                        ${TRIO_DEFAULT_PREFIX}/lib/${TRIO_LIBRARY_NAME} 
                        CACHE FILEPATH ${TRIO_LIBRARY_DOC} FORCE
                        )
        endif()
        set(TRIO_IS_A_TARGET YES)
endif()

get_filename_component(TRIO_PREFIX "${TRIO_LIBRARY}" PATH)
# TODO: i want to cannonize this test into a function.
if(DEFINED CMAKE_CONFIGURATION_TYPES)
        get_filename_component(TRIO_PREFIX "${TRIO_PREFIX}/../.." ABSOLUTE)
else()
        get_filename_component(TRIO_PREFIX "${TRIO_PREFIX}/.." ABSOLUTE)
endif()

include_directories("${TRIO_DEFAULT_PREFIX}/include")

function(add_dependency_on_trio TARGET)
        set(TRIO_LIBRARIES 
                ${TRIO_LIBRARY}
                )
        if(TRIO_IS_A_TARGET)
                add_dependencies(${TARGET} trio)
        endif()
        get_target_property(Type ${TARGET} TYPE)
        if(Type STREQUAL EXECUTABLE OR Type STREQUAL SHARED_LIBRARY)
                if(UNIX)
                        set(dependencies m)
                endif()
                target_link_libraries(${TARGET} ${TRIO_LIBRARY} ${dependencies})
        endif()
endfunction()

diff -Naur ../externalproj_eg/cmake/trio.cmake ./cmake/trio.cmake
--- ../externalproj_eg/cmake/trio.cmake	2011-10-09 12:39:29.366780912 +0200
+++ ./cmake/trio.cmake	2011-10-09 12:41:58.100076162 +0200
@@ -122,6 +122,7 @@
 	else()
 		message(FATAL_ERROR "i don't know how to compile an external project on this platform.")
 	endif()
+        ADD_LIBRARY(libtrio STATIC IMPORTED)
 	if(DEFINED CMAKE_CONFIGURATION_TYPES)
 		foreach(i ${CMAKE_CONFIGURATION_TYPES})
 			# WORKAROUND: target_link_libraries() doesn't recognize configuration
@@ -130,14 +131,21 @@
 			# RelWithDebugInfo for everything else. unfortunately, this is 
 			# going to cause ExternalProject.cmake to compile unnecessary
 			# versions of trio for other configurations.
-			if(i STREQUAL Debug)
-				list(APPEND trio_libs debug)
-				list(APPEND trio_libs "${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}")
-			elseif(i STREQUAL RelWithDebInfo)
-				list(APPEND trio_libs optimized)
-				list(APPEND trio_libs "${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}")
-			endif()
+			# if(i STREQUAL Debug)
+			# 	list(APPEND trio_libs debug)
+			# 	list(APPEND trio_libs "${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}")
+			# elseif(i STREQUAL RelWithDebInfo)
+			# 	list(APPEND trio_libs optimized)
+			# 	list(APPEND trio_libs "${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}")
+			# endif()
+                        STRING(TOUPPER "${i}" j)
+                        SET_TARGET_PROPERTIES(
+                            libtrio
+                            PROPERTIES
+                            IMPORTED_LOCATION_${j} "${TRIO_DEFAULT_PREFIX}/lib/${i}/${TRIO_LIBRARY_NAME}"
+                        )
 		endforeach()
+                LIST(APPEND trio_libs libtrio)
 		set(TRIO_LIBRARY 
 			${trio_libs} 
 			CACHE STRING ${TRIO_LIBRARY_DOC} FORCE
@@ -159,7 +167,7 @@
 	get_filename_component(TRIO_PREFIX "${TRIO_PREFIX}/.." ABSOLUTE)
 endif()
 
-include_directories("${TRIO_PREFIX}/include")
+include_directories("${TRIO_DEFAULT_PREFIX}/include")
 
 function(add_dependency_on_trio TARGET)
 	set(TRIO_LIBRARIES 
--
Powered by www.kitware.com

Visit other Kitware open-source projects at 
http://www.kitware.com/opensource/opensource.html

Please keep messages on-topic and check the CMake FAQ at: 
http://www.cmake.org/Wiki/CMake_FAQ

Follow this link to subscribe/unsubscribe:
http://www.cmake.org/mailman/listinfo/cmake

Reply via email to