Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -170,6 +170,7 @@ set(LIBCXX_LINK_FLAGS "") set(LIBCXX_LIBRARIES "") + # Configure compiler. include(config-ix) @@ -258,16 +259,8 @@ # Feature flags =============================================================== define_if(MSVC -D_CRT_SECURE_NO_WARNINGS) -define_if_not(LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE -D_LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE) -define_if_not(LIBCXX_ENABLE_STDIN -D_LIBCPP_HAS_NO_STDIN) -define_if_not(LIBCXX_ENABLE_STDOUT -D_LIBCPP_HAS_NO_STDOUT) -define_if_not(LIBCXX_ENABLE_THREADS -D_LIBCPP_HAS_NO_THREADS) -define_if_not(LIBCXX_ENABLE_MONOTONIC_CLOCK -D_LIBCPP_HAS_NO_MONOTONIC_CLOCK) -define_if_not(LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS -D_LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS) - - -# Sanitizer flags ============================================================= +# Sanitizer flags ============================================================== # Configure for sanitizers. If LIBCXX_BUILT_STANDALONE then we have to do # the flag translation ourselves. Othewise LLVM's CMakeList.txt will handle it. if (LIBCXX_BUILT_STANDALONE) @@ -301,6 +294,25 @@ message(WARNING "LLVM_USE_SANITIZER is not supported on this platform.") endif() endif() + + +# Configuration file flags ===================================================== +config_define_if_not(LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE) +config_define_if_not(LIBCXX_ENABLE_STDIN _LIBCPP_HAS_NO_STDIN) +config_define_if_not(LIBCXX_ENABLE_STDOUT _LIBCPP_HAS_NO_STDOUT) +config_define_if_not(LIBCXX_ENABLE_THREADS _LIBCPP_HAS_NO_THREADS) +config_define_if_not(LIBCXX_ENABLE_MONOTONIC_CLOCK _LIBCPP_HAS_NO_MONOTONIC_CLOCK) +config_define_if_not(LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS) + +if (LIBCXX_NEEDS_SITE_CONFIG) + set(CS_HEADER ${LIBCXX_BINARY_DIR}/__config_site) + configure_file( + include/__config_site.in + ${CS_HEADER} + @ONLY) + add_compile_flags(-include ${CS_HEADER}) +endif() + #=============================================================================== # Setup Source Code And Tests #=============================================================================== Index: cmake/Modules/HandleLibcxxFlags.cmake =================================================================== --- cmake/Modules/HandleLibcxxFlags.cmake +++ cmake/Modules/HandleLibcxxFlags.cmake @@ -49,6 +49,20 @@ endif() endmacro() +macro(config_define_if condition def) + if (${condition}) + set(${def} ON) + set(LIBCXX_NEEDS_SITE_CONFIG ON) + endif() +endmacro() + +macro(config_define_if_not condition def) + if (NOT ${condition}) + set(${def} ON) + set(LIBCXX_NEEDS_SITE_CONFIG ON) + endif() +endmacro() + # Add a specified list of flags to both 'LIBCXX_COMPILE_FLAGS' and # 'LIBCXX_LINK_FLAGS'. macro(add_flags) Index: docs/DesignDocs/SupportingCustomConfigurations.rst =================================================================== --- /dev/null +++ docs/DesignDocs/SupportingCustomConfigurations.rst @@ -0,0 +1,90 @@ +============================================================= +Capturing configuration information during installation +======================================================== + + +.. contents:: + :local: + +The Problem +=========== + +Currently the libc++ supports building the library with a number of different +configuration options. Unfortunately all of that configuration information is +lost when libc++ is installed. In order to support "persistent" +configurations libc++ needs a mechanism to capture the configuration options +in the INSTALLED headers. + + +Design Goals +============ + +* The solution should not INSTALL any additional headers. We don't want an extra + #include slowing everybody down. + +* The solution should not unduly affect libc++ developers. The problem is limited + to installed versions of libc++ and the solution should be as well. + +* The solution should not modify any existing headers EXCEPT during installation. + It makes developers lives harder if they have to regenerate the libc++ headers + every time they are modified. + +* The solution should not make any of the libc++ headers dependant on + files generated by the build system. The headers should be able to compile + out of the box without any modification. + +* The solution should not have ANY effect on users who don't need special + configuration options. The vast majority of users will never need this so it + shouldn't cost them. + + +The Solution +============ + +When you first configure libc++ using CMake we check to see if we need to +capture any options. If we haven't been given any "persistent" options then +we do NOTHING. + +Otherwise we create a custom installation rule that modifies the installed __config +header. The rule first generates a dummy "__config_site" header containing the required +#defines. The contents of the dummy header are then prependend to the installed +__config header. By manually prepending the files we avoid the cost of an +extra #include and we allow the __config header to be ignorant of the extra + configuration all together. An example "__config" header generated when +-DLIBCXX_ENABLE_THREADS=OFF is given to CMake would look something like: + +.. code-block:: cpp + + //===----------------------------------------------------------------------===// + // + // The LLVM Compiler Infrastructure + // + // This file is dual licensed under the MIT and the University of Illinois Open + // Source Licenses. See LICENSE.TXT for details. + // + //===----------------------------------------------------------------------===// + + #ifndef _LIBCPP_CONFIG_SITE + #define _LIBCPP_CONFIG_SITE + + /* #undef _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE */ + /* #undef _LIBCPP_HAS_NO_STDIN */ + /* #undef _LIBCPP_HAS_NO_STDOUT */ + #define _LIBCPP_HAS_NO_THREADS + /* #undef _LIBCPP_HAS_NO_MONOTONIC_CLOCK */ + /* #undef _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS */ + + #endif + // -*- C++ -*- + //===--------------------------- __config ---------------------------------===// + // + // The LLVM Compiler Infrastructure + // + // This file is dual licensed under the MIT and the University of Illinois Open + // Source Licenses. See LICENSE.TXT for details. + // + //===----------------------------------------------------------------------===// + + #ifndef _LIBCPP_CONFIG + #define _LIBCPP_CONFIG + Index: docs/index.rst =================================================================== --- docs/index.rst +++ docs/index.rst @@ -124,6 +124,12 @@ Design Documents ---------------- +.. toctree:: + :maxdepth: 1 + + DesignDocs/SupportingCustomConfigurations + + * ` design `_ * ` design `_ * `Status of debug mode `_ Index: include/CMakeLists.txt =================================================================== --- include/CMakeLists.txt +++ include/CMakeLists.txt @@ -1,19 +1,23 @@ if (NOT LIBCXX_INSTALL_SUPPORT_HEADERS) set(LIBCXX_SUPPORT_HEADER_PATTERN PATTERN "support" EXCLUDE) endif() + + set(LIBCXX_HEADER_PATTERN PATTERN "*" PATTERN "CMakeLists.txt" EXCLUDE PATTERN ".svn" EXCLUDE + PATTERN "__config_site.in" EXCLUDE ${LIBCXX_SUPPORT_HEADER_PATTERN} ) file(COPY . - DESTINATION "${CMAKE_BINARY_DIR}/include/c++/v1" + DESTINATION include/c++/v1 FILES_MATCHING ${LIBCXX_HEADER_PATTERN} ) + if (LIBCXX_INSTALL_HEADERS) install(DIRECTORY . DESTINATION include/c++/v1 @@ -22,4 +26,25 @@ ${LIBCXX_HEADER_PATTERN} PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) + + if (LIBCXX_NEEDS_SITE_CONFIG) + # Generate and install a custom __config header. The new header is created + # by prepending __config_site to the current __config header. + # TODO(EricWF) Is it portable to use "cat" and ">>"? + add_custom_command(OUTPUT ${LIBCXX_BINARY_DIR}/__generated_config + COMMAND ${CMAKE_COMMAND} -E copy ${LIBCXX_BINARY_DIR}/__config_site ${LIBCXX_BINARY_DIR}/__generated_config + COMMAND cat ${LIBCXX_SOURCE_DIR}/include/__config >> ${LIBCXX_BINARY_DIR}/__generated_config + DEPENDS ${LIBCXX_SOURCE_DIR}/include/__config + ) + # Add a target that executes the generation commands. + add_custom_target(generate_config_header ALL + DEPENDS ${LIBCXX_BINARY_DIR}/__generated_config) + # Install the generated header as __config. + install(FILES ${LIBCXX_BINARY_DIR}/__generated_config + DESTINATION include/c++/v1 + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + RENAME __config + COMPONENT libcxx) + endif() + endif() Index: include/__config_site.in =================================================================== --- /dev/null +++ include/__config_site.in @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_CONFIG_SITE +#define _LIBCPP_CONFIG_SITE + +#cmakedefine _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE +#cmakedefine _LIBCPP_HAS_NO_STDIN +#cmakedefine _LIBCPP_HAS_NO_STDOUT +#cmakedefine _LIBCPP_HAS_NO_THREADS +#cmakedefine _LIBCPP_HAS_NO_MONOTONIC_CLOCK +#cmakedefine _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS + +#endif Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -25,6 +25,7 @@ # The tests shouldn't link to any ABI library when it has been linked into # libc++ statically. if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY) + message(FATAL_ERROR "SHIT") set(LIBCXX_CXX_ABI_LIBNAME "none") endif() set(LIBCXX_TARGET_INFO "libcxx.test.target_info.LocalTI" CACHE STRING Index: test/libcxx/test/config.py =================================================================== --- test/libcxx/test/config.py +++ test/libcxx/test/config.py @@ -357,24 +357,10 @@ # Configure feature flags. self.configure_compile_flags_exceptions() self.configure_compile_flags_rtti() - self.configure_compile_flags_no_global_filesystem_namespace() - self.configure_compile_flags_no_stdin() - self.configure_compile_flags_no_stdout() + enable_32bit = self.get_lit_bool('enable_32bit', False) if enable_32bit: self.cxx.flags += ['-m32'] - # Configure threading features. - enable_threads = self.get_lit_bool('enable_threads', True) - enable_monotonic_clock = self.get_lit_bool('enable_monotonic_clock', - True) - if not enable_threads: - self.configure_compile_flags_no_threads() - if not enable_monotonic_clock: - self.configure_compile_flags_no_monotonic_clock() - elif not enable_monotonic_clock: - self.lit_config.fatal('enable_monotonic_clock cannot be false when' - ' enable_threads is true.') - self.configure_compile_flags_no_thread_unsafe_c_functions() # Use verbose output for better errors self.cxx.flags += ['-v'] @@ -391,13 +377,46 @@ support_path = os.path.join(self.libcxx_src_root, 'test/support') self.cxx.compile_flags += ['-I' + support_path] self.cxx.compile_flags += ['-include', os.path.join(support_path, 'nasty_macros.hpp')] + + # Check for the presence of a __config_site header in the build directory + # If one exists we need to manually include in all of the tests. + config_site_header = os.path.join(self.libcxx_obj_root, '__config_site') + if os.path.isfile(config_site_header): + self.lit_config.note('Using __config_site header in %s' % + os.path.dirname(config_site_header)) + # FIXME: deduce_config_site_features wont work after we add + # -include
below. + self.deduce_config_site_features(config_site_header) + self.cxx.compile_flags += ['-include', config_site_header] + libcxx_headers = self.get_lit_conf( 'libcxx_headers', os.path.join(self.libcxx_src_root, 'include')) + if not os.path.isdir(libcxx_headers): self.lit_config.fatal("libcxx_headers='%s' is not a directory." % libcxx_headers) self.cxx.compile_flags += ['-I' + libcxx_headers] + def deduce_config_site_features(self, header): + """ deduce_config_site_features - Deduce and add the test features that + that are implied by the #define's in the __config_site header. + """ + predefines = self.cxx.dumpMacros() + macros = self.cxx.dumpMacros(header) + feature_macros = set(macros.keys()) - set(predefines.keys()) + # We expect the header guard to be one of the definitions + assert '_LIBCPP_CONFIG_SITE' in feature_macros + feature_macros.remove('_LIBCPP_CONFIG_SITE') + # The __config_site header should be non-empty. Otherwise it should + # have never been emitted by CMake. + assert len(feature_macros) > 0 + # Transform each macro name into the feature name used in the tests. + # Ex. _LIBCPP_HAS_NO_THREADS -> libcpp-has-no-threads + for m in feature_macros: + assert m.startswith('_LIBCPP_HAS') + m = m.lower()[1:].replace('_', '-') + self.config.available_features.add(m) + def configure_compile_flags_exceptions(self): enable_exceptions = self.get_lit_bool('enable_exceptions', True) if not enable_exceptions: @@ -410,43 +429,7 @@ self.config.available_features.add('libcpp-no-rtti') self.cxx.compile_flags += ['-fno-rtti', '-D_LIBCPP_NO_RTTI'] - def configure_compile_flags_no_global_filesystem_namespace(self): - enable_global_filesystem_namespace = self.get_lit_bool( - 'enable_global_filesystem_namespace', True) - if not enable_global_filesystem_namespace: - self.config.available_features.add( - 'libcpp-has-no-global-filesystem-namespace') - self.cxx.compile_flags += [ - '-D_LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE'] - - def configure_compile_flags_no_stdin(self): - enable_stdin = self.get_lit_bool('enable_stdin', True) - if not enable_stdin: - self.config.available_features.add('libcpp-has-no-stdin') - self.cxx.compile_flags += ['-D_LIBCPP_HAS_NO_STDIN'] - - def configure_compile_flags_no_stdout(self): - enable_stdout = self.get_lit_bool('enable_stdout', True) - if not enable_stdout: - self.config.available_features.add('libcpp-has-no-stdout') - self.cxx.compile_flags += ['-D_LIBCPP_HAS_NO_STDOUT'] - - def configure_compile_flags_no_threads(self): - self.cxx.compile_flags += ['-D_LIBCPP_HAS_NO_THREADS'] - self.config.available_features.add('libcpp-has-no-threads') - - def configure_compile_flags_no_thread_unsafe_c_functions(self): - enable_thread_unsafe_c_functions = self.get_lit_bool( - 'enable_thread_unsafe_c_functions', True) - if not enable_thread_unsafe_c_functions: - self.cxx.compile_flags += [ - '-D_LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS'] - self.config.available_features.add( - 'libcpp-has-no-thread-unsafe-c-functions') - def configure_compile_flags_no_monotonic_clock(self): - self.cxx.compile_flags += ['-D_LIBCPP_HAS_NO_MONOTONIC_CLOCK'] - self.config.available_features.add('libcpp-has-no-monotonic-clock') def configure_link_flags(self): no_default_flags = self.get_lit_bool('no_default_flags', False) Index: test/lit.site.cfg.in =================================================================== --- test/lit.site.cfg.in +++ test/lit.site.cfg.in @@ -7,12 +7,6 @@ config.enable_rtti = "@LIBCXX_ENABLE_RTTI@" config.enable_shared = "@LIBCXX_ENABLE_SHARED@" config.enable_32bit = "@LIBCXX_BUILD_32_BITS@" -config.enable_global_filesystem_namespace = "@LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE@" -config.enable_stdin = "@LIBCXX_ENABLE_STDIN@" -config.enable_stdout = "@LIBCXX_ENABLE_STDOUT@" -config.enable_threads = "@LIBCXX_ENABLE_THREADS@" -config.enable_thread_unsafe_c_functions = "@LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS@" -config.enable_monotonic_clock = "@LIBCXX_ENABLE_MONOTONIC_CLOCK@" config.cxx_abi = "@LIBCXX_CXX_ABI_LIBNAME@" config.use_sanitizer = "@LLVM_USE_SANITIZER@" config.sanitizer_library = "@LIBCXX_SANITIZER_LIBRARY@"