Skip to content

Commit

Permalink
CMake: put all binaries into a dedicated location in the build dir.
Browse files Browse the repository at this point in the history
This makes it possible to:

 - finally use Corrade as a CMake subproject on Windows and have your
   executables not fail to run with a "DLL missing" error (and the
   setting is put to cache so superprojects just implicitly make use of
   that)
 - run tests on Windows without having to install first
 - use (future) dynamic plugins from a CMake subproject on any platform
   without having to install first or load them by filename --- and the
   plugin directory can be easily discovered as relative to
   libraryLocation() of the library implementing given plugin interface
  • Loading branch information
mosra committed Oct 10, 2019
1 parent eaf27be commit 73d3f31
Show file tree
Hide file tree
Showing 24 changed files with 214 additions and 96 deletions.
31 changes: 31 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,37 @@ set(CORRADE_INCLUDE_INSTALL_DIR ${CORRADE_INCLUDE_INSTALL_PREFIX}/include/Corrad
set(CORRADE_LIBRARY_VERSION 2.2)
set(CORRADE_LIBRARY_SOVERSION 2)

# A single output location. After a decade of saying NO THIS IS A NON-SOLUTION
# TO A NON-PROBLEM I reconsidered my views and enabled this, because:
#
# - On Windows (which don't have RPATH), this makes test execution finally
# possible without having to install all the stuff first (including the
# test-only libs, which is ugh).
# - With CMake subprojects, this makes it finally possible to use dynamic
# plugins directly from the build dir (again without installing anything) ---
# all plugins are put into the same place, so PluginManager has a single
# place to look into; and thanks to the dynamic libraries being there as
# well, this location can be automagically detected as relative to
# Directory::libraryLocation().
# - Thanks to the $<CONFIG> being part of the output path, you are always sure
# you never accidentally mix up debug/release libraries when switching
# CMAKE_BUILD_TYPE in an existing build dir.
#
# The runtime location is set to CMAKE_BINARY_DIR and not PROJECT_BINARY_DIR
# because have one runtime location per CMake subproject would not solve much
# either. If the user already provides CMAKE_RUNTIME_OUTPUT_DIRECTORY (even
# empty), it's respected and nothing is being done.
#
# Explicitly using a generator expression to ensure plugins are added to e.g.
# <CONFIG>/lib/corrade/fs/ instead of lib/corrade/fs/<CONFIG>. Also adding this
# to cache, making superprojects pick that up implicitly as well, without
# forcing them to explicitly mirror this setting.
if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/bin CACHE PATH "" FORCE)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/lib CACHE PATH "" FORCE)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$<CONFIG>/lib CACHE PATH "" FORCE)
endif()

add_subdirectory(modules)
add_subdirectory(src)

Expand Down
13 changes: 8 additions & 5 deletions doc/building-corrade.dox
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ Selected Corrade functionality is available as single-header libraries for
easier integration into your projects. See @ref corrade-singles for more
information.

@section building-corrade-cmake-subproject Using Corrade as a CMake subproject

Corrade can be bundled in your project with the @cb{.cmake} add_subdirectory() @ce
CMake command. See @ref corrade-cmake-subproject for more information.

@section building-corrade-packages Prepared packages

The easiest way to install Corrade is to use one of the ready-made packages for
Expand Down Expand Up @@ -600,10 +605,8 @@ subdirectories in the build directory) or using
ctest --output-on-failure
@endcode

in the build directory. On Windows the tests require the library to be
installed with DLLs accessible through @cb{.bat} %PATH% @ce. See the
@ref building-corrade-windows "above Windows documentation" for more
information.
in the build directory. It's not needed to install anything anywhere to run the
tests.

@subsection building-corrade-doc Building documentation

Expand Down Expand Up @@ -866,7 +869,7 @@ through `PATH`.

If you set `CMAKE_INSTALL_PREFIX` to `/usr` subdirectory of the particular
Android platform sysroot, the package will get found automatically when
compiling subprojects. Gradle and other Android buildsystems expect
compiling depending projects. Gradle and other Android buildsystems expect
platform-independent includes and other files to be stored in a central
location, you can set `CORRADE_INCLUDE_INSTALL_PREFIX` to `/usr` subdirectory of
the global NDK sysroot. Another option is to explicitly set `CMAKE_PREFIX_PATH`
Expand Down
51 changes: 49 additions & 2 deletions doc/corrade-cmake.dox
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,20 @@ namespace Corrade {
@tableofcontents

Corrade uses CMake as a primary build system for both building and integration
into your projects. The main logic is in the
[FindCorrade.cmake](https://github.com/mosra/corrade/blob/master/modules/FindCorrade.cmake)
into your projects.

@section corrade-cmake-installed Using Corrade that was externally built and installed

The main logic is in the [FindCorrade.cmake](https://github.com/mosra/corrade/blob/master/modules/FindCorrade.cmake)
module distributed with the engine in the `modules/` directory, you are
encouraged to copy it into your project and add path to the file to
`CMAKE_MODULE_PATH`:

@code{.cmake}
# Path where FindCorrade.cmake can be found, adapt as needed
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/modules/" ${CMAKE_MODULE_PATH})

find_package(Corrade REQUIRED ...) # see below
@endcode

Otherwise, if CMake won't be able to find this file in predefined locations, it
Expand All @@ -61,6 +66,48 @@ If you installed the library to non-standard location (other than `/usr`, e.g.
`/home/xyz/projects`), set `CMAKE_PREFIX_PATH` to that directory to help CMake
find it.

@section corrade-cmake-subproject Using Corrade as a CMake subproject

A self-contained alternative to the above is adding the Corrade repository into
your project (as a Git submodule, bundling a downloaded archive etc.) and use
@cb{.cmake} add_subdirectory() @ce. With that approach you don't need to care
about `FindCorrade.cmake`, however the usual tradeoffs when bundling code apply
--- slower full rebuilds, IDEs having more to parse etc.

@code{.cmake}
add_subdirectory(corrade EXCLUDE_FROM_ALL) # so only things you use are built

find_package(Corrade REQUIRED ...) # see below
@endcode

Note that the @cb{.cmake} find_package() @ce call is needed in both the
installed and the subproject case for a properly configured environment.

To simplify your project setup, the subproject globally configures
@m_class{m-doc-external} [CMAKE_RUNTIME_OUTPUT_DIRECTORY](https://cmake.org/cmake/help/latest/variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY.html)
and friends to `<CONFIG>/bin` / `<CONFIG>/lib` directories inside your build
directory. This makes the subproject workflow easier when dynamically-loaded
plugins are involved; and on Windows it makes it possible to run built
executables without having to do a @cb{.sh} $PATH @ce setup for dependency
DLLs. If your project already configures `CMAKE_{RUNTIME,LIBRARY,ARCHIVE}_OUTPUT_DIRECTORY`,
those will get used instead (and you can also set your own output directories
* *after* the @cb{.cmake} add_subdirectory() @ce call, which will make Corrade
keep the above). If you want to disable this behavior altogether and keep
all executables and libraries in their implicit locations, set those variables
to an empty string (as opposed to nothing at all, which is the same as if the
variable is not set) --- Corrade will detect and respect that:

@code{.cmake}
# I'm happy with having binaries scattered around the build dir
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "")

add_subdirectory(corrade EXCLUDE_FROM_ALL)
@endcode

@section corrade-cmake-find-module Finding the package and its component

Basic usage is:

@code{.cmake}
Expand Down
10 changes: 9 additions & 1 deletion modules/UseCorrade.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,15 @@ function(corrade_add_test test_name)
# The EFFECTIVE_PLATFORM_NAME variable is not expanded when using
# TARGET_* generator expressions on iOS, we need to hardcode it
# manually. See http://public.kitware.com/pipermail/cmake/2016-March/063049.html
add_test(NAME ${test_name} COMMAND ${XCTest_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>${_CORRADE_EFFECTIVE_PLATFORM_NAME}/${test_name}Runner.xctest)
# In case we redirect the runtime output directory, use that (and
# assume there's no TARGET_* generator expression). This will of
# course break when someone sets the LIBRARY_OUTPUT_DIRECTORY
# property of the target, but that didn't work before either.
if(CMAKE_LIBRARY_OUTPUT_DIRECTORY)
add_test(NAME ${test_name} COMMAND ${XCTest_EXECUTABLE} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${test_name}Runner.xctest)
else()
add_test(NAME ${test_name} COMMAND ${XCTest_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>${_CORRADE_EFFECTIVE_PLATFORM_NAME}/${test_name}Runner.xctest)
endif()
else()
xctest_add_test(${test_name} ${test_name}Runner)
endif()
Expand Down
4 changes: 3 additions & 1 deletion package/ci/appveyor-desktop-mingw.bat
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ cmake .. ^
-DBUILD_TESTS=ON ^
-G Ninja || exit /b
cmake --build . || exit /b
cmake --build . --target install || exit /b

rem Test
cd %APPVEYOR_BUILD_FOLDER%/build || exit /b
set CORRADE_TEST_COLOR=ON
ctest -V || exit /b

rem Test install, after running the tests as for them it shouldn't be needed
cmake --build . --target install || exit /b

rem Examples. The --coverage flag needs to be specified as well otherwise
rem linking to CorradeMain will result in undefined reference to __gcov_init
rem and such.
Expand Down
4 changes: 3 additions & 1 deletion package/ci/appveyor-desktop.bat
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ cmake .. ^
-DBUILD_STATIC=%BUILD_STATIC% ^
-G Ninja || exit /b
cmake --build . || exit /b
cmake --build . --target install || exit /b

rem Test
set CORRADE_TEST_COLOR=ON
ctest -V || exit /b

rem Test install, after running the tests as for them it shouldn't be needed
cmake --build . --target install || exit /b

rem Examples
cd %APPVEYOR_BUILD_FOLDER% || exit /b
mkdir build-examples && cd build-examples || exit /b
Expand Down
3 changes: 3 additions & 0 deletions package/ci/appveyor-rt.bat
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ cmake .. ^
-DBUILD_STATIC=ON ^
-G "%GENERATOR%" || exit /b
cmake --build . --config Release || exit /b

rem Test install, after running the tests as for them it shouldn't be needed
cmake --build . --config Release --target install || exit /b
4 changes: 4 additions & 0 deletions package/ci/travis-android-arm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ cmake .. \
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a \
-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang \
-DCMAKE_ANDROID_STL_TYPE=c++_static \
-DCMAKE_INSTALL_PREFIX=$HOME/deps \
-DCMAKE_BUILD_TYPE=Release \
-DCORRADE_RC_EXECUTABLE=$HOME/deps-native/bin/corrade-rc \
-DBUILD_TESTS=ON \
Expand All @@ -34,3 +35,6 @@ echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
emulator -avd test -no-audio -no-window &
android-wait-for-emulator
CORRADE_TEST_COLOR=ON ctest -V

# Test install, after running the tests as for them it shouldn't be needed
ninja install
6 changes: 5 additions & 1 deletion package/ci/travis-desktop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ cmake .. \
-DBUILD_STATIC=$BUILD_STATIC \
-DCMAKE_BUILD_TYPE=Debug \
-G Ninja
ninja install
ninja
ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always" TSAN_OPTIONS="color=always suppressions=$TRAVIS_BUILD_DIR/package/ci/threadsanitizer.conf" CORRADE_TEST_COLOR=ON ctest -V

# Test install, after running the tests as for them it shouldn't be needed
ninja install

cd ..

# Examples
Expand Down
4 changes: 4 additions & 0 deletions package/ci/travis-emscripten.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ cmake .. \
-DEMSCRIPTEN_PREFIX=$(echo /usr/local/Cellar/emscripten/*/libexec) \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS_RELEASE="-DNDEBUG -O1" \
-DCMAKE_INSTALL_PREFIX=$HOME/deps \
-DCMAKE_EXE_LINKER_FLAGS_RELEASE="-O1" \
-DBUILD_TESTS=ON \
-G Ninja
ninja -j4

# Test
CORRADE_TEST_COLOR=ON ctest -V

# Test install, after running the tests as for them it shouldn't be needed
ninja install
4 changes: 4 additions & 0 deletions package/ci/travis-ios-simulator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ cmake .. \
-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk \
-DCMAKE_OSX_ARCHITECTURES="x86_64" \
-DCORRADE_RC_EXECUTABLE=$HOME/deps-native/bin/corrade-rc \
-DCMAKE_INSTALL_PREFIX=$HOME/deps \
-DBUILD_STATIC=ON \
-DBUILD_TESTS=ON \
-DTESTSUITE_TARGET_XCTEST=ON \
-G Xcode
set -o pipefail && cmake --build . --config Release | xcpretty
CORRADE_TEST_COLOR=ON ctest -V -C Release

# Test install, after running the tests as for them it shouldn't be needed
set -o pipefail && cmake --build . --config Release --target install | xcpretty
9 changes: 0 additions & 9 deletions src/Corrade/PluginManager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,6 @@ if(BUILD_TESTS)
target_link_libraries(CorradePluginManagerTestLib ${CMAKE_DL_LIBS})
endif()

# On Windows we need to install first and then run the tests to avoid "DLL
# not found" hell, thus we need to install this too
if(CORRADE_TARGET_WINDOWS AND NOT CMAKE_CROSSCOMPILING AND NOT BUILD_STATIC)
install(TARGETS CorradePluginManagerTestLib
RUNTIME DESTINATION ${CORRADE_BINARY_INSTALL_DIR}
LIBRARY DESTINATION ${CORRADE_LIBRARY_INSTALL_DIR}
ARCHIVE DESTINATION ${CORRADE_LIBRARY_INSTALL_DIR})
endif()

add_subdirectory(Test)
endif()

Expand Down
49 changes: 20 additions & 29 deletions src/Corrade/PluginManager/Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,33 @@
# CMake before 3.8 has broken $<TARGET_FILE*> expressions for iOS (see
# https://gitlab.kitware.com/cmake/cmake/merge_requests/404) and since Corrade
# doesn't support dynamic plugins on iOS, this sorta works around that. Should
# be revisited when updating Travis to newer Xcode (current has CMake 3.6).
# be revisited when updating Travis to newer Xcode (xcode7.3 has CMake 3.6).
if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
set(PLUGINS_DIR $<TARGET_FILE_DIR:PluginManagerManagerTest>)
# On windows, all DLLs are in bin/, but elsewhere in lib/. See the root
# CMakeLists.txt for more info about where this is set to.
if(CORRADE_TARGET_WINDOWS)
set(PLUGINS_DIR ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
else()
set(PLUGINS_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
endif()
set(DOG_PLUGIN_FILENAME $<TARGET_FILE:Dog>)
set(DOGGO_PLUGIN_FILENAME $<TARGET_FILE:Doggo>)
set(PITBULL_PLUGIN_FILENAME $<TARGET_FILE:PitBull>)

# First replace ${} variables, then $<> generator expressions
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/configure.h.in)
file(GENERATE OUTPUT $<TARGET_FILE_DIR:PluginManagerManagerTest>/configure.h
INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in)
else()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/configure.h)
endif()

# First replace ${} variables, then $<> generator expressions
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/configure.h.in)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/configure.h
INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in)

corrade_add_test(PluginManagerManagerTest
ManagerTest.cpp
AbstractAnimal.cpp
AbstractDeletable.cpp
AbstractFood.cpp
LIBRARIES CorradePluginManagerTestLib Canary)
if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
target_include_directories(PluginManagerManagerTest PRIVATE $<TARGET_FILE_DIR:PluginManagerManagerTest>)
else()
target_include_directories(PluginManagerManagerTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif()
target_include_directories(PluginManagerManagerTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(NOT CORRADE_TARGET_EMSCRIPTEN)
find_package(Threads REQUIRED)
target_link_libraries(PluginManagerManagerTest PRIVATE Threads::Threads)
Expand All @@ -63,28 +62,20 @@ corrade_add_test(PluginManagerManagerInitFiniTest
ManagerInitFiniTest.cpp
LIBRARIES InitFiniStatic CorradePluginManager)
if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
target_include_directories(PluginManagerManagerInitFiniTest PRIVATE $<TARGET_FILE_DIR:PluginManagerManagerTest>)
target_include_directories(PluginManagerManagerInitFiniTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
endif()

corrade_add_test(PluginManagerImportStaticTest
AbstractAnimal.cpp
ImportStaticTest.cpp
LIBRARIES CorradePluginManagerTestLib Canary Dird)
if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
target_include_directories(PluginManagerImportStaticTest PRIVATE $<TARGET_FILE_DIR:PluginManagerManagerTest>)
else()
target_include_directories(PluginManagerImportStaticTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif()
target_include_directories(PluginManagerImportStaticTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)

corrade_add_test(PluginManagerAbstractPluginTest
AbstractAnimal.cpp
AbstractPluginTest.cpp
LIBRARIES CorradePluginManagerTestLib Canary)
if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
target_include_directories(PluginManagerAbstractPluginTest PRIVATE $<TARGET_FILE_DIR:PluginManagerManagerTest>)
else()
target_include_directories(PluginManagerAbstractPluginTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif()
target_include_directories(PluginManagerAbstractPluginTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)

set_target_properties(
PluginManagerManagerTest
Expand Down Expand Up @@ -116,13 +107,13 @@ if(CORRADE_BUILD_STATIC AND NOT CORRADE_TARGET_EMSCRIPTEN AND NOT CORRADE_TARGET
target_link_libraries(PluginManagerGlobalStateAcrossLibrariesLibrary
PUBLIC CorradePluginManagerTestLib
PRIVATE Canary)
target_include_directories(PluginManagerGlobalStateAcrossLibrariesLibrary PRIVATE $<TARGET_FILE_DIR:PluginManagerManagerTest>)
target_include_directories(PluginManagerGlobalStateAcrossLibrariesLibrary PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)

corrade_add_test(PluginManagerGlobalStateAcrossLi___Test
AbstractAnimal.cpp
GlobalStateAcrossLibrariesTest.cpp
LIBRARIES PluginManagerGlobalStateAcrossLibrariesLibrary)
target_include_directories(PluginManagerGlobalStateAcrossLi___Test PRIVATE $<TARGET_FILE_DIR:PluginManagerManagerTest>)
target_include_directories(PluginManagerGlobalStateAcrossLi___Test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
set_target_properties(
PluginManagerGlobalStateAcrossLibrariesLibrary
PluginManagerGlobalStateAcrossLi___Test
Expand Down
8 changes: 1 addition & 7 deletions src/Corrade/PluginManager/Test/ManagerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,13 +301,7 @@ struct WrongPlugin: AbstractPlugin {
static std::string pluginInterface() { return {}; }

static std::vector<std::string> pluginSearchPaths() {
return {
#ifndef CMAKE_INTDIR
Utility::Directory::join(PLUGINS_DIR, "wrong")
#else
Utility::Directory::join(Utility::Directory::join(PLUGINS_DIR, "wrong"), CMAKE_INTDIR)
#endif
};
return {Utility::Directory::join(PLUGINS_DIR, "wrong")};
}
};

Expand Down
Loading

0 comments on commit 73d3f31

Please sign in to comment.