diff options
202 files changed, 7505 insertions, 3677 deletions
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 1b0aa14..d4f644a 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -146,8 +146,10 @@ The options are: Specify the primary input source file to the command. This is treated just like any value given to the ``DEPENDS`` option but also suggests to Visual Studio generators where to hang - the custom command. At most one custom command may specify a - given source file as its main dependency. + the custom command. Each source file may have at most one command + specifying it as its main dependency. A compile command (i.e. for a + library or an executable) counts as an implicit main dependency which + gets silently overwritten by a custom command specification. ``OUTPUT`` Specify the output files the command is expected to produce. diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index dc5621a..0f6d4cf 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -97,21 +97,35 @@ Available logical expressions are: compile features and a list of supported compilers. ``$<COMPILE_LANGUAGE:lang>`` ``1`` when the language used for compilation unit matches ``lang``, - otherwise ``0``. This expression may be used to specify compile options for - source files of a particular language in a target. For example, to specify - the use of the ``-fno-exceptions`` compile option (compiler id checks - elided): + otherwise ``0``. This expression may be used to specify compile options, + compile definitions, and include directories for source files of a + particular language in a target. For example: .. code-block:: cmake - add_executable(myapp main.cpp foo.c bar.cpp) + add_executable(myapp main.cpp foo.c bar.cpp zot.cu) target_compile_options(myapp PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions> ) + target_compile_definitions(myapp + PRIVATE $<$<COMPILE_LANGUAGE:CXX>:COMPILING_CXX> + $<$<COMPILE_LANGUAGE:CUDA>:COMPILING_CUDA> + ) + target_include_directories(myapp + PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/opt/foo/cxx_headers> + ) + + This specifies the use of the ``-fno-exceptions`` compile option, + ``COMPILING_CXX`` compile definition, and ``cxx_headers`` include + directory for C++ only (compiler id checks elided). It also specifies + a ``COMPILING_CUDA`` compile definition for CUDA. - Note that with :ref:`Visual Studio Generators` there is no way to represent + Note that with :ref:`Visual Studio Generators` and :generator:`Xcode` there + is no way to represent target-wide compile definitions or include directories + separately for ``C`` and ``CXX`` languages. + Also, with :ref:`Visual Studio Generators` there is no way to represent target-wide flags separately for ``C`` and ``CXX`` languages. Under these - generators, target-wide flags for both C and C++ sources will be evaluated + generators, expressions for both C and C++ sources will be evaluated using ``CXX`` if there are any C++ sources and otherwise using ``C``. A workaround is to create separate libraries for each source file language instead: @@ -124,20 +138,6 @@ Available logical expressions are: add_executable(myapp main.cpp) target_link_libraries(myapp myapp_c myapp_cxx) - The ``Makefile`` and ``Ninja`` based generators can also use this - expression to specify compile-language specific compile definitions - and include directories: - - .. code-block:: cmake - - add_executable(myapp main.cpp foo.c bar.cpp) - target_compile_definitions(myapp - PRIVATE $<$<COMPILE_LANGUAGE:CXX>:COMPILING_CXX> - ) - target_include_directories(myapp - PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/opt/foo/cxx_headers> - ) - Informational Expressions ========================= diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 2c63c4b..88b39d4 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -121,6 +121,7 @@ Properties on Targets /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG /prop_tgt/ARCHIVE_OUTPUT_NAME /prop_tgt/AUTOGEN_BUILD_DIR + /prop_tgt/AUTOGEN_PARALLEL /prop_tgt/AUTOGEN_TARGET_DEPENDS /prop_tgt/AUTOMOC_COMPILER_PREDEFINES /prop_tgt/AUTOMOC_DEPEND_FILTERS diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst index cafeae1..e5c593f 100644 --- a/Help/manual/cmake-qt.7.rst +++ b/Help/manual/cmake-qt.7.rst @@ -96,7 +96,8 @@ following targets by setting the :variable:`CMAKE_AUTOMOC` variable. The options to pass to ``moc``. The :variable:`CMAKE_AUTOMOC_MOC_OPTIONS` variable may be populated to pre-set the options for all following targets. -Additional macro names to search for can be added to :prop_tgt:`AUTOMOC_MACRO_NAMES`. +Additional macro names to search for can be added to +:prop_tgt:`AUTOMOC_MACRO_NAMES`. Additional ``moc`` dependency file names can be extracted from source code by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 3f57612..c18d8da 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -276,6 +276,7 @@ Variables that Control the Build /variable/CMAKE_ANDROID_STL_TYPE /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_AUTOGEN_PARALLEL /variable/CMAKE_AUTOMOC /variable/CMAKE_AUTOMOC_COMPILER_PREDEFINES /variable/CMAKE_AUTOMOC_DEPEND_FILTERS diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index ff8c6c7..c2e6435 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -337,7 +337,7 @@ Available commands are: ``paxr`` (restricted pax, default), and ``zip``. ``time <command> [<args>...]`` - Run command and return elapsed time. + Run command and display elapsed time. ``touch <file>`` Touch a file. diff --git a/Help/prop_tgt/AUTOGEN_PARALLEL.rst b/Help/prop_tgt/AUTOGEN_PARALLEL.rst new file mode 100644 index 0000000..07fbc5a --- /dev/null +++ b/Help/prop_tgt/AUTOGEN_PARALLEL.rst @@ -0,0 +1,21 @@ +AUTOGEN_PARALLEL +---------------- + +Number of parallel ``moc`` or ``uic`` processes to start when using +:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`. + +The custom `<origin>_autogen` target starts a number of threads of which +each one parses a source file and on demand starts a ``moc`` or ``uic`` +process. :prop_tgt:`AUTOGEN_PARALLEL` controls how many parallel threads +(and therefore ``moc`` or ``uic`` processes) are started. + +- An empty (or unset) value or the string ``AUTO`` sets the number of + threads/processes to the number of physical CPUs on the host system. +- A positive non zero integer value sets the exact thread/process count. +- Otherwise a single thread/process is started. + +By default :prop_tgt:`AUTOGEN_PARALLEL` is initialized from +:variable:`CMAKE_AUTOGEN_PARALLEL`. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index 9e5261f..3bd693a 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst @@ -71,7 +71,8 @@ automoc targets together in an IDE, e.g. in MSVS. The global property :prop_gbl:`AUTOGEN_SOURCE_GROUP` can be used to group files generated by :prop_tgt:`AUTOMOC` together in an IDE, e.g. in MSVS. -Additional macro names to search for can be added to :prop_tgt:`AUTOMOC_MACRO_NAMES`. +Additional macro names to search for can be added to +:prop_tgt:`AUTOMOC_MACRO_NAMES`. Additional ``moc`` dependency file names can be extracted from source code by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. @@ -82,5 +83,8 @@ which is controlled by :prop_tgt:`AUTOMOC_COMPILER_PREDEFINES`. Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`. +The number of parallel ``moc`` processes to start can be modified by +setting :prop_tgt:`AUTOGEN_PARALLEL`. + See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst index 1791384..4fc603f 100644 --- a/Help/prop_tgt/AUTOUIC.rst +++ b/Help/prop_tgt/AUTOUIC.rst @@ -33,5 +33,8 @@ autouic targets together in an IDE, e.g. in MSVS. Source files can be excluded from :prop_tgt:`AUTOUIC` processing by enabling :prop_sf:`SKIP_AUTOUIC` or the broader :prop_sf:`SKIP_AUTOGEN`. +The number of parallel ``uic`` processes to start can be modified by +setting :prop_tgt:`AUTOGEN_PARALLEL`. + See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/release/dev/CheckIncludeFile-required-libs.rst b/Help/release/dev/CheckIncludeFile-required-libs.rst new file mode 100644 index 0000000..14c43d1 --- /dev/null +++ b/Help/release/dev/CheckIncludeFile-required-libs.rst @@ -0,0 +1,11 @@ +CheckIncludeFile-required-libs +------------------------------ + +* The :module:`CheckIncludeFile` module ``check_include_file`` macro + learned to honor the ``CMAKE_REQUIRED_LIBRARIES`` variable. + +* The :module:`CheckIncludeFileCXX` module ``check_include_file_cxx`` macro + learned to honor the ``CMAKE_REQUIRED_LIBRARIES`` variable. + +* The :module:`CheckIncludeFiles` module ``check_include_files`` macro + learned to honor the ``CMAKE_REQUIRED_LIBRARIES`` variable. diff --git a/Help/release/dev/autogen-parallel.rst b/Help/release/dev/autogen-parallel.rst new file mode 100644 index 0000000..50ae9a6 --- /dev/null +++ b/Help/release/dev/autogen-parallel.rst @@ -0,0 +1,10 @@ +autogen-parallel +---------------- + +* When using :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC`, CMake starts multiple + parallel ``moc`` or ``uic`` processes to reduce the build time. + The new :variable:`CMAKE_AUTOGEN_PARALLEL` variable and + :prop_tgt:`AUTOGEN_PARALLEL` target property allow to modify the number of + parallel ``moc`` or ``uic`` processes to start. + By default CMake starts a single ``moc`` or ``uic`` process for each physical + CPU on the host system. diff --git a/Help/release/dev/cache-newline.rst b/Help/release/dev/cache-newline.rst new file mode 100644 index 0000000..96900bb --- /dev/null +++ b/Help/release/dev/cache-newline.rst @@ -0,0 +1,7 @@ +cache-newline +------------- + +* Variables containing newlines in their values now get truncated before the + newline when they are written to the cache file. In addition, a warning + comment is written to the cache file, and a warning message is displayed to + the user on the console. diff --git a/Help/release/dev/extend-compile-language-genex.rst b/Help/release/dev/extend-compile-language-genex.rst index 0a0a669..7c09376 100644 --- a/Help/release/dev/extend-compile-language-genex.rst +++ b/Help/release/dev/extend-compile-language-genex.rst @@ -1,7 +1,14 @@ extend-compile-language-genex ----------------------------- -* The ``COMPILE_LANGUAGE`` :manual:`generator expression - <cmake-generator-expressions(7)>` may now be used with - :ref:`Visual Studio Generators` in :prop_tgt:`COMPILE_OPTIONS` - and :command:`file(GENERATE)`. +* :ref:`Visual Studio Generators` learned to support the ``COMPILE_LANGUAGE`` + :manual:`generator expression <cmake-generator-expressions(7)>` in + target-wide :prop_tgt:`COMPILE_DEFINITIONS`, + :prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_OPTIONS`, and + :command:`file(GENERATE)`. + +* The :generator:`Xcode` generator learned to support the ``COMPILE_LANGUAGE`` + :manual:`generator expression <cmake-generator-expressions(7)>` in + target-wide :prop_tgt:`COMPILE_DEFINITIONS` and + :prop_tgt:`INCLUDE_DIRECTORIES`. It previously supported only + :prop_tgt:`COMPILE_OPTIONS` and :command:`file(GENERATE)`. diff --git a/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst b/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst new file mode 100644 index 0000000..dd9499a --- /dev/null +++ b/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst @@ -0,0 +1,10 @@ +CMAKE_AUTOGEN_PARALLEL +---------------------- + +Number of parallel ``moc`` or ``uic`` processes to start when using +:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`. + +This variable is used to initialize the :prop_tgt:`AUTOGEN_PARALLEL` property +on all the targets. See that target property for additional information. + +By default :variable:`CMAKE_AUTOGEN_PARALLEL` is unset. diff --git a/Help/variable/CMAKE_AUTOMOC_MACRO_NAMES.rst b/Help/variable/CMAKE_AUTOMOC_MACRO_NAMES.rst index 5b79c40..2c8997b 100644 --- a/Help/variable/CMAKE_AUTOMOC_MACRO_NAMES.rst +++ b/Help/variable/CMAKE_AUTOMOC_MACRO_NAMES.rst @@ -12,7 +12,7 @@ information. The default value is ``Q_OBJECT;Q_GADGET;Q_NAMESPACE``. Example -------- +^^^^^^^ Let CMake know that source files that contain ``CUSTOM_MACRO`` must be ``moc`` processed as well:: diff --git a/Help/variable/CMAKE_CROSSCOMPILING.rst b/Help/variable/CMAKE_CROSSCOMPILING.rst index cf9865b..9e96769 100644 --- a/Help/variable/CMAKE_CROSSCOMPILING.rst +++ b/Help/variable/CMAKE_CROSSCOMPILING.rst @@ -1,8 +1,26 @@ CMAKE_CROSSCOMPILING -------------------- -Is CMake currently cross compiling. +Intended to indicate whether CMake is cross compiling, but note limitations +discussed below. -This variable will be set to true by CMake if CMake is cross -compiling. Specifically if the build platform is different from the -target platform. +This variable will be set to true by CMake if the :variable:`CMAKE_SYSTEM_NAME` +variable has been set manually (i.e. in a toolchain file or as a cache entry +from the :manual:`cmake <cmake(1)>` command line). In most cases, manually +setting :variable:`CMAKE_SYSTEM_NAME` will only be done when cross compiling, +since it will otherwise be given the same value as +:variable:`CMAKE_HOST_SYSTEM_NAME` if not manually set, which is correct for +the non-cross-compiling case. In the event that :variable:`CMAKE_SYSTEM_NAME` +is manually set to the same value as :variable:`CMAKE_HOST_SYSTEM_NAME`, then +``CMAKE_CROSSCOMPILING`` will still be set to true. + +Another case to be aware of is that builds targeting Apple platforms other than +macOS are handled differently to other cross compiling scenarios. Rather than +relying on :variable:`CMAKE_SYSTEM_NAME` to select the target platform, Apple +device builds use :variable:`CMAKE_OSX_SYSROOT` to select the appropriate SDK, +which indirectly determines the target platform. Furthermore, when using the +Xcode generator, developers can switch between device and simulator builds at +build time rather than having a single choice at configure time, so the concept +of whether the build is cross compiling or not is more complex. Therefore, the +use of ``CMAKE_CROSSCOMPILING`` is not recommended for projects targeting Apple +devices. diff --git a/Help/variable/CMAKE_LANG_COMPILER_EXTERNAL_TOOLCHAIN.rst b/Help/variable/CMAKE_LANG_COMPILER_EXTERNAL_TOOLCHAIN.rst index 5be935b..cbe3544 100644 --- a/Help/variable/CMAKE_LANG_COMPILER_EXTERNAL_TOOLCHAIN.rst +++ b/Help/variable/CMAKE_LANG_COMPILER_EXTERNAL_TOOLCHAIN.rst @@ -6,7 +6,7 @@ The external toolchain for cross-compiling, if supported. Some compiler toolchains do not ship their own auxiliary utilities such as archivers and linkers. The compiler driver may support a command-line argument to specify the location of such tools. -``CMAKE_<LANG>_COMPILER_EXTERNAL_TOOLCHAIN`` may be set to a path to a path to +``CMAKE_<LANG>_COMPILER_EXTERNAL_TOOLCHAIN`` may be set to a path to the external toolchain and will be passed to the compiler driver if supported. This variable may only be set in a toolchain file specified by diff --git a/Modules/AutoRccInfo.cmake.in b/Modules/AutoRccInfo.cmake.in index 5457a6f..cbab4a7 100644 --- a/Modules/AutoRccInfo.cmake.in +++ b/Modules/AutoRccInfo.cmake.in @@ -1,10 +1,6 @@ # Meta set(ARCC_MULTI_CONFIG @_multi_config@) # Directories and files -set(ARCC_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") -set(ARCC_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") -set(ARCC_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/") -set(ARCC_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/") set(ARCC_BUILD_DIR @_build_dir@) # Qt environment set(ARCC_RCC_EXECUTABLE @_qt_rcc_executable@) diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 9a4a06d..7320c0a 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -1,5 +1,6 @@ # Meta set(AM_MULTI_CONFIG @_multi_config@) +set(AM_PARALLEL @_parallel@) # Directories and files set(AM_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") set(AM_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") diff --git a/Modules/CheckIncludeFile.cmake b/Modules/CheckIncludeFile.cmake index e5554c4..501fc9a 100644 --- a/Modules/CheckIncludeFile.cmake +++ b/Modules/CheckIncludeFile.cmake @@ -27,6 +27,8 @@ # list of macros to define (-DFOO=bar) # ``CMAKE_REQUIRED_INCLUDES`` # list of include directories +# ``CMAKE_REQUIRED_LIBRARIES`` +# list of libraries to link # ``CMAKE_REQUIRED_QUIET`` # execute quietly without messages # @@ -59,6 +61,7 @@ macro(CHECK_INCLUDE_FILE INCLUDE VARIABLE) ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} "${CHECK_INCLUDE_FILE_C_INCLUDE_DIRS}" diff --git a/Modules/CheckIncludeFileCXX.cmake b/Modules/CheckIncludeFileCXX.cmake index 7948bab..cdb25fb 100644 --- a/Modules/CheckIncludeFileCXX.cmake +++ b/Modules/CheckIncludeFileCXX.cmake @@ -27,6 +27,8 @@ # list of macros to define (-DFOO=bar) # ``CMAKE_REQUIRED_INCLUDES`` # list of include directories +# ``CMAKE_REQUIRED_LIBRARIES`` +# list of libraries to link # ``CMAKE_REQUIRED_QUIET`` # execute quietly without messages # @@ -58,6 +60,7 @@ macro(CHECK_INCLUDE_FILE_CXX INCLUDE VARIABLE) ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} "${CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS}" diff --git a/Modules/CheckIncludeFiles.cmake b/Modules/CheckIncludeFiles.cmake index 59afdab..14db68c 100644 --- a/Modules/CheckIncludeFiles.cmake +++ b/Modules/CheckIncludeFiles.cmake @@ -33,6 +33,8 @@ # list of macros to define (-DFOO=bar) # ``CMAKE_REQUIRED_INCLUDES`` # list of include directories +# ``CMAKE_REQUIRED_LIBRARIES`` +# list of libraries to link # ``CMAKE_REQUIRED_QUIET`` # execute quietly without messages # @@ -102,6 +104,7 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE) ${CMAKE_BINARY_DIR} ${src} COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILES_FLAGS} "${CHECK_INCLUDE_FILES_INCLUDE_DIRS}" diff --git a/Modules/FindDoxygen.cmake b/Modules/FindDoxygen.cmake index 8aea4e0..599d799 100644 --- a/Modules/FindDoxygen.cmake +++ b/Modules/FindDoxygen.cmake @@ -411,6 +411,8 @@ macro(_Doxygen_find_doxygen) "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\doxygen_is1;Inno Setup: App Path]/bin" /Applications/Doxygen.app/Contents/Resources /Applications/Doxygen.app/Contents/MacOS + /Applications/Utilities/Doxygen.app/Contents/Resources + /Applications/Utilities/Doxygen.app/Contents/MacOS DOC "Doxygen documentation generation tool (http://www.doxygen.org)" ) mark_as_advanced(DOXYGEN_EXECUTABLE) @@ -492,8 +494,11 @@ macro(_Doxygen_find_dot) "C:/Program Files/ATT/Graphviz/bin" [HKEY_LOCAL_MACHINE\\SOFTWARE\\ATT\\Graphviz;InstallPath]/bin /Applications/Graphviz.app/Contents/MacOS + /Applications/Utilities/Graphviz.app/Contents/MacOS /Applications/Doxygen.app/Contents/Resources /Applications/Doxygen.app/Contents/MacOS + /Applications/Utilities/Doxygen.app/Contents/Resources + /Applications/Utilities/Doxygen.app/Contents/MacOS DOC "Dot tool for use with Doxygen" ) mark_as_advanced(DOXYGEN_DOT_EXECUTABLE) diff --git a/Modules/FindJava.cmake b/Modules/FindJava.cmake index b913e17..e3f5af6 100644 --- a/Modules/FindJava.cmake +++ b/Modules/FindJava.cmake @@ -237,10 +237,18 @@ if(Java_FIND_COMPONENTS) endif() elseif(component STREQUAL "Development") list(APPEND _JAVA_REQUIRED_VARS Java_JAVA_EXECUTABLE Java_JAVAC_EXECUTABLE - Java_JAVAH_EXECUTABLE Java_JAVADOC_EXECUTABLE) - if(Java_JAVA_EXECUTABLE AND Java_JAVAC_EXECUTABLE - AND Java_JAVAH_EXECUTABLE AND Java_JAVADOC_EXECUTABLE) - set(Java_Development_FOUND TRUE) + Java_JAVADOC_EXECUTABLE) + if(Java_VERSION VERSION_LESS "1.10") + list(APPEND _JAVA_REQUIRED_VARS Java_JAVAH_EXECUTABLE) + if(Java_JAVA_EXECUTABLE AND Java_JAVAC_EXECUTABLE + AND Java_JAVAH_EXECUTABLE AND Java_JAVADOC_EXECUTABLE) + set(Java_Development_FOUND TRUE) + endif() + else() + if(Java_JAVA_EXECUTABLE AND Java_JAVAC_EXECUTABLE + AND Java_JAVADOC_EXECUTABLE) + set(Java_Development_FOUND TRUE) + endif() endif() elseif(component STREQUAL "IdlJ") list(APPEND _JAVA_REQUIRED_VARS Java_IDLJ_EXECUTABLE) @@ -268,11 +276,19 @@ if(Java_FIND_COMPONENTS) endif() else() # Check for Development - find_package_handle_standard_args(Java - REQUIRED_VARS Java_JAVA_EXECUTABLE Java_JAR_EXECUTABLE Java_JAVAC_EXECUTABLE - Java_JAVAH_EXECUTABLE Java_JAVADOC_EXECUTABLE - VERSION_VAR Java_VERSION_STRING - ) + if(Java_VERSION VERSION_LESS "1.10") + find_package_handle_standard_args(Java + REQUIRED_VARS Java_JAVA_EXECUTABLE Java_JAR_EXECUTABLE Java_JAVAC_EXECUTABLE + Java_JAVAH_EXECUTABLE Java_JAVADOC_EXECUTABLE + VERSION_VAR Java_VERSION_STRING + ) + else() + find_package_handle_standard_args(Java + REQUIRED_VARS Java_JAVA_EXECUTABLE Java_JAR_EXECUTABLE Java_JAVAC_EXECUTABLE + Java_JAVADOC_EXECUTABLE + VERSION_VAR Java_VERSION_STRING + ) + endif() endif() diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake index 9ccd46b..4d0786c 100644 --- a/Modules/FindOpenGL.cmake +++ b/Modules/FindOpenGL.cmake @@ -22,7 +22,6 @@ # Defined to the platform-specific OpenGL libraries if the system has OpenGL. # ``OpenGL::OpenGL`` # Defined to libOpenGL if the system is GLVND-based. -# ``OpenGL::GL`` # ``OpenGL::GLU`` # Defined if the system has GLU. # ``OpenGL::GLX`` diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index 893ddc6..ced092e 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake @@ -470,7 +470,7 @@ foreach(LANG IN LISTS OpenMP_FINDLIST) if(OpenMP_${LANG}_FLAGS) separate_arguments(_OpenMP_${LANG}_OPTIONS NATIVE_COMMAND "${OpenMP_${LANG}_FLAGS}") set_property(TARGET OpenMP::OpenMP_${LANG} PROPERTY - INTERFACE_COMPILE_OPTIONS "${_OpenMP_${LANG}_OPTIONS}") + INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:${LANG}>:${_OpenMP_${LANG}_OPTIONS}>") unset(_OpenMP_${LANG}_OPTIONS) endif() if(OpenMP_${LANG}_LIBRARIES) diff --git a/Modules/FindPythonInterp.cmake b/Modules/FindPythonInterp.cmake index 64b98a8..3ef8e9f 100644 --- a/Modules/FindPythonInterp.cmake +++ b/Modules/FindPythonInterp.cmake @@ -100,6 +100,9 @@ if(NOT PYTHON_EXECUTABLE) [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-32\\InstallPath] [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-64\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-32\\InstallPath] + [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_CURRENT_VERSION}-64\\InstallPath] ) endforeach() endif() diff --git a/Modules/FindTCL.cmake b/Modules/FindTCL.cmake index 80779b3..19eb932 100644 --- a/Modules/FindTCL.cmake +++ b/Modules/FindTCL.cmake @@ -116,7 +116,7 @@ find_library(TCL_LIBRARY NAMES tcl tcl${TCL_LIBRARY_VERSION} tcl${TCL_TCLSH_VERSION} tcl${TK_WISH_VERSION} - tcl86 tcl8.6 + tcl86 tcl8.6 tcl86t tcl8.6t tcl85 tcl8.5 tcl84 tcl8.4 tcl83 tcl8.3 @@ -130,7 +130,7 @@ find_library(TK_LIBRARY NAMES tk tk${TK_LIBRARY_VERSION} tk${TCL_TCLSH_VERSION} tk${TK_WISH_VERSION} - tk86 tk8.6 + tk86 tk8.6 tk86t tk8.6t tk85 tk8.5 tk84 tk8.4 tk83 tk8.3 diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake index 7f08c4a..0abbb61 100644 --- a/Modules/Platform/AIX-GNU.cmake +++ b/Modules/Platform/AIX-GNU.cmake @@ -24,5 +24,7 @@ macro(__aix_compiler_gnu lang) set(CMAKE_${lang}_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH 1) set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") - unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY) + if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 7 OR CMAKE_SYSTEM_VERSION VERSION_LESS 7.1) + unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY) + endif() endmacro() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1b46377..cd1287c 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -313,8 +313,8 @@ set(SRCS cmQtAutoGen.h cmQtAutoGenerator.cxx cmQtAutoGenerator.h - cmQtAutoGeneratorInitializer.cxx - cmQtAutoGeneratorInitializer.h + cmQtAutoGenInitializer.cxx + cmQtAutoGenInitializer.h cmQtAutoGeneratorMocUic.cxx cmQtAutoGeneratorMocUic.h cmQtAutoGeneratorRcc.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 76fd7c4..b41f400 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 10) -set(CMake_VERSION_PATCH 20180114) +set(CMake_VERSION_PATCH 20180122) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index c8806a7..e332a77 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -5,19 +5,19 @@ #include "cmCTest.h" #include "cmCTestRunTest.h" #include "cmCTestTestHandler.h" -#include "cmProcessOutput.h" #include "cmsys/Process.h" #include <algorithm> #include <fcntl.h> #include <iostream> #include <signal.h> -#include <stdint.h> #include <string> #if !defined(_WIN32) #include <unistd.h> #endif +#define CM_PROCESS_BUF_SIZE 65536 + #if defined(_WIN32) && !defined(__CYGWIN__) #include <io.h> @@ -60,6 +60,7 @@ static int cmProcessGetPipes(int* fds) cmProcess::cmProcess(cmCTestRunTest& runner) : Runner(runner) + , Conv(cmProcessOutput::UTF8, CM_PROCESS_BUF_SIZE) { this->Timeout = std::chrono::duration<double>::zero(); this->TotalTime = std::chrono::duration<double>::zero(); @@ -232,9 +233,7 @@ void cmProcess::OnRead(ssize_t nread, const uv_buf_t* buf) std::string line; if (nread > 0) { std::string strdata; - cmProcessOutput processOutput(cmProcessOutput::UTF8, - static_cast<unsigned int>(buf->len)); - processOutput.DecodeText(buf->base, static_cast<size_t>(nread), strdata); + this->Conv.DecodeText(buf->base, static_cast<size_t>(nread), strdata); this->Output.insert(this->Output.end(), strdata.begin(), strdata.end()); while (this->Output.GetLine(line)) { @@ -245,6 +244,10 @@ void cmProcess::OnRead(ssize_t nread, const uv_buf_t* buf) return; } + if (nread == 0) { + return; + } + // The process will provide no more data. if (nread != UV_EOF) { auto error = static_cast<int>(nread); @@ -258,6 +261,7 @@ void cmProcess::OnRead(ssize_t nread, const uv_buf_t* buf) } this->ReadHandleClosed = true; + this->PipeReader.reset(); if (this->ProcessHandleClosed) { uv_timer_stop(this->Timer); this->Runner.FinalizeTest(); @@ -271,10 +275,10 @@ void cmProcess::OnAllocateCB(uv_handle_t* handle, size_t suggested_size, self->OnAllocate(suggested_size, buf); } -void cmProcess::OnAllocate(size_t suggested_size, uv_buf_t* buf) +void cmProcess::OnAllocate(size_t /*suggested_size*/, uv_buf_t* buf) { - if (this->Buf.size() < suggested_size) { - this->Buf.resize(suggested_size); + if (this->Buf.size() != CM_PROCESS_BUF_SIZE) { + this->Buf.resize(CM_PROCESS_BUF_SIZE); } *buf = diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h index 9250896..633be24 100644 --- a/Source/CTest/cmProcess.h +++ b/Source/CTest/cmProcess.h @@ -5,13 +5,14 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmProcessOutput.h" #include "cmUVHandlePtr.h" #include "cm_uv.h" #include <chrono> #include <stddef.h> +#include <stdint.h> #include <string> -#include <sys/types.h> #include <vector> class cmCTestRunTest; @@ -80,6 +81,7 @@ private: std::vector<char> Buf; cmCTestRunTest& Runner; + cmProcessOutput Conv; int Signal = 0; cmProcess::State ProcessState = cmProcess::State::Starting; diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index 44095ec..64aa46e 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx @@ -10,6 +10,7 @@ #include <string.h> #include "cmGeneratedFileStream.h" +#include "cmMessenger.h" #include "cmState.h" #include "cmSystemTools.h" #include "cmVersion.h" @@ -205,7 +206,8 @@ bool cmCacheManager::ReadPropertyEntry(std::string const& entryKey, return false; } -void cmCacheManager::WritePropertyEntries(std::ostream& os, CacheIterator i) +void cmCacheManager::WritePropertyEntries(std::ostream& os, CacheIterator i, + cmMessenger* messenger) { for (const char** p = this->PersistentProperties; *p; ++p) { if (const char* value = i.GetProperty(*p)) { @@ -221,11 +223,13 @@ void cmCacheManager::WritePropertyEntries(std::ostream& os, CacheIterator i) os << ":INTERNAL="; this->OutputValue(os, value); os << "\n"; + cmCacheManager::OutputNewlineTruncationWarning(os, key, value, + messenger); } } } -bool cmCacheManager::SaveCache(const std::string& path) +bool cmCacheManager::SaveCache(const std::string& path, cmMessenger* messenger) { std::string cacheFile = path; cacheFile += "/CMakeCache.txt"; @@ -316,7 +320,10 @@ bool cmCacheManager::SaveCache(const std::string& path) this->OutputKey(fout, i.first); fout << ":" << cmState::CacheEntryTypeToString(t) << "="; this->OutputValue(fout, ce.Value); - fout << "\n\n"; + fout << "\n"; + cmCacheManager::OutputNewlineTruncationWarning(fout, i.first, ce.Value, + messenger); + fout << "\n"; } } @@ -333,7 +340,7 @@ bool cmCacheManager::SaveCache(const std::string& path) } cmStateEnums::CacheEntryType t = i.GetType(); - this->WritePropertyEntries(fout, i); + this->WritePropertyEntries(fout, i, messenger); if (t == cmStateEnums::INTERNAL) { // Format is key:type=value if (const char* help = i.GetProperty("HELPSTRING")) { @@ -343,6 +350,8 @@ bool cmCacheManager::SaveCache(const std::string& path) fout << ":" << cmState::CacheEntryTypeToString(t) << "="; this->OutputValue(fout, i.GetValue()); fout << "\n"; + cmCacheManager::OutputNewlineTruncationWarning(fout, i.GetName(), + i.GetValue(), messenger); } } fout << "\n"; @@ -390,6 +399,19 @@ void cmCacheManager::OutputKey(std::ostream& fout, std::string const& key) void cmCacheManager::OutputValue(std::ostream& fout, std::string const& value) { + // look for and truncate newlines + std::string::size_type newline = value.find('\n'); + if (newline != std::string::npos) { + std::string truncated = value.substr(0, newline); + OutputValueNoNewlines(fout, truncated); + } else { + OutputValueNoNewlines(fout, value); + } +} + +void cmCacheManager::OutputValueNoNewlines(std::ostream& fout, + std::string const& value) +{ // if value has trailing space or tab, enclose it in single quotes if (!value.empty() && (value[value.size() - 1] == ' ' || value[value.size() - 1] == '\t')) { @@ -423,6 +445,50 @@ void cmCacheManager::OutputHelpString(std::ostream& fout, } } +void cmCacheManager::OutputWarningComment(std::ostream& fout, + std::string const& message, + bool wrapSpaces) +{ + std::string::size_type end = message.size(); + std::string oneLine; + std::string::size_type pos = 0; + for (std::string::size_type i = 0; i <= end; i++) { + if ((i == end) || (message[i] == '\n') || + ((i - pos >= 60) && (message[i] == ' ') && wrapSpaces)) { + fout << "# "; + if (message[pos] == '\n') { + pos++; + fout << "\\n"; + } + oneLine = message.substr(pos, i - pos); + fout << oneLine << "\n"; + pos = i; + } + } +} + +void cmCacheManager::OutputNewlineTruncationWarning(std::ostream& fout, + std::string const& key, + std::string const& value, + cmMessenger* messenger) +{ + if (value.find('\n') != std::string::npos) { + if (messenger) { + std::string message = "Value of "; + message += key; + message += " contained a newline; truncating"; + messenger->IssueMessage(cmake::WARNING, message); + } + + std::string comment = "WARNING: Value of "; + comment += key; + comment += " contained a newline and was truncated. Original value:"; + + OutputWarningComment(fout, comment, true); + OutputWarningComment(fout, value, false); + } +} + void cmCacheManager::RemoveCacheEntry(const std::string& key) { CacheEntryMap::iterator i = this->Cache.find(key); diff --git a/Source/cmCacheManager.h b/Source/cmCacheManager.h index 28cba85..73923d1 100644 --- a/Source/cmCacheManager.h +++ b/Source/cmCacheManager.h @@ -15,7 +15,7 @@ #include "cmPropertyMap.h" #include "cmStateTypes.h" -class cmake; +class cmMessenger; /** \class cmCacheManager * \brief Control class for cmake's cache @@ -108,7 +108,7 @@ public: std::set<std::string>& includes); ///! Save cache for given makefile. Saves to output path/CMakeCache.txt - bool SaveCache(const std::string& path); + bool SaveCache(const std::string& path, cmMessenger* messenger); ///! Delete the cache given bool DeleteCache(const std::string& path); @@ -218,16 +218,25 @@ protected: unsigned int CacheMinorVersion; private: - cmake* CMakeInstance; typedef std::map<std::string, CacheEntry> CacheEntryMap; static void OutputHelpString(std::ostream& fout, const std::string& helpString); + static void OutputWarningComment(std::ostream& fout, + std::string const& message, + bool wrapSpaces); + static void OutputNewlineTruncationWarning(std::ostream& fout, + std::string const& key, + std::string const& value, + cmMessenger* messenger); static void OutputKey(std::ostream& fout, std::string const& key); static void OutputValue(std::ostream& fout, std::string const& value); + static void OutputValueNoNewlines(std::ostream& fout, + std::string const& value); static const char* PersistentProperties[]; bool ReadPropertyEntry(std::string const& key, CacheEntry& e); - void WritePropertyEntries(std::ostream& os, CacheIterator i); + void WritePropertyEntries(std::ostream& os, CacheIterator i, + cmMessenger* messenger); CacheEntryMap Cache; // Only cmake and cmState should be able to add cache values diff --git a/Source/cmGeneratorExpressionEvaluationFile.cxx b/Source/cmGeneratorExpressionEvaluationFile.cxx index 87b6b34..c988969 100644 --- a/Source/cmGeneratorExpressionEvaluationFile.cxx +++ b/Source/cmGeneratorExpressionEvaluationFile.cxx @@ -13,6 +13,7 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmSourceFile.h" +#include "cmSourceFileLocationKind.h" #include "cmSystemTools.h" #include "cmake.h" @@ -102,7 +103,8 @@ void cmGeneratorExpressionEvaluationFile::CreateOutputFile( for (std::string const& le : enabledLanguages) { std::string name = this->OutputFileExpr->Evaluate( lg, config, false, nullptr, nullptr, nullptr, le); - cmSourceFile* sf = lg->GetMakefile()->GetOrCreateSource(name); + cmSourceFile* sf = lg->GetMakefile()->GetOrCreateSource( + name, false, cmSourceFileLocationKind::Known); sf->SetProperty("GENERATED", "1"); gg->SetFilenameTargetDepends( diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 3d311d6..34ef45f 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -805,7 +805,7 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (context->Language.empty()) { reportError( @@ -827,33 +827,14 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode return std::string(); } std::string genName = gg->GetName(); - if (genName.find("Visual Studio") != std::string::npos) { - if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() || - dagChecker->EvaluatingIncludeDirectories())) { - reportError( - context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS " - "and file(GENERATE) with the Visual Studio generator."); - return std::string(); - } - } else if (genName.find("Xcode") != std::string::npos) { - if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() || - dagChecker->EvaluatingIncludeDirectories())) { - reportError( - context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS " - "and file(GENERATE) with the Xcode generator."); - return std::string(); - } - } else { - if (genName.find("Makefiles") == std::string::npos && - genName.find("Ninja") == std::string::npos && - genName.find("Watcom WMake") == std::string::npos) { - reportError( - context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> not supported for this generator."); - return std::string(); - } + if (genName.find("Makefiles") == std::string::npos && + genName.find("Ninja") == std::string::npos && + genName.find("Visual Studio") == std::string::npos && + genName.find("Xcode") == std::string::npos && + genName.find("Watcom WMake") == std::string::npos) { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_LANGUAGE:...> not supported for this generator."); + return std::string(); } if (parameters.empty()) { return context->Language; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index fd9b488..47a9390 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -33,7 +33,7 @@ #include "cmMakefile.h" #include "cmOutputConverter.h" #include "cmPolicies.h" -#include "cmQtAutoGeneratorInitializer.h" +#include "cmQtAutoGenInitializer.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateDirectory.h" @@ -1473,10 +1473,10 @@ bool cmGlobalGenerator::ComputeTargetDepends() return true; } -std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>> +std::vector<std::unique_ptr<cmQtAutoGenInitializer>> cmGlobalGenerator::CreateQtAutoGenInitializers() { - std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>> autogenInits; + std::vector<std::unique_ptr<cmQtAutoGenInitializer>> autogenInits; #ifdef CMAKE_BUILD_WITH_CMAKE for (cmLocalGenerator* localGen : this->LocalGenerators) { @@ -1506,13 +1506,13 @@ cmGlobalGenerator::CreateQtAutoGenInitializers() } std::string qtVersionMajor = - cmQtAutoGeneratorInitializer::GetQtMajorVersion(target); + cmQtAutoGenInitializer::GetQtMajorVersion(target); // don't do anything if there is no Qt4 or Qt5Core (which contains moc) if (qtVersionMajor != "4" && qtVersionMajor != "5") { continue; } - autogenInits.emplace_back(new cmQtAutoGeneratorInitializer( + autogenInits.emplace_back(new cmQtAutoGenInitializer( target, mocEnabled, uicEnabled, rccEnabled, qtVersionMajor)); } } diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 92e6a29..3ebff82 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -32,7 +32,7 @@ class cmLinkLineComputer; class cmLocalGenerator; class cmMakefile; class cmOutputConverter; -class cmQtAutoGeneratorInitializer; +class cmQtAutoGenInitializer; class cmSourceFile; class cmStateDirectory; class cmake; @@ -437,7 +437,7 @@ protected: virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; // Qt auto generators - std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>> + std::vector<std::unique_ptr<cmQtAutoGenInitializer>> CreateQtAutoGenInitializers(); std::string SelectMakeProgram(const std::string& makeProgram, diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 338c2b4..bd51f60 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1703,6 +1703,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, gtgt->GetName().c_str()); return; } + std::string const& langForPreprocessor = llang; if (gtgt->IsIPOEnabled(llang, configName)) { const char* ltoValue = @@ -1723,7 +1724,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, this->AppendDefines(ppDefs, exportMacro); } std::vector<std::string> targetDefines; - gtgt->GetCompileDefinitions(targetDefines, configName, "C"); + if (!langForPreprocessor.empty()) { + gtgt->GetCompileDefinitions(targetDefines, configName, + langForPreprocessor); + } this->AppendDefines(ppDefs, targetDefines); buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS", ppDefs.CreateList()); @@ -1996,8 +2000,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, const bool emitSystemIncludes = this->XcodeVersion >= 83; std::vector<std::string> includes; - this->CurrentLocalGenerator->GetIncludeDirectories(includes, gtgt, "C", - configName); + if (!langForPreprocessor.empty()) { + this->CurrentLocalGenerator->GetIncludeDirectories( + includes, gtgt, langForPreprocessor, configName); + } std::set<std::string> emitted; emitted.insert("/System/Library/Frameworks"); diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index eccd4d0..59c20a9 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -637,7 +637,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( configType = projectType; } std::string flags; - if (strcmp(configType, "10") != 0) { + std::string langForClCompile; + if (target->GetType() <= cmStateEnums::OBJECT_LIBRARY) { const std::string& linkLanguage = (this->FortranProject ? std::string("Fortran") : target->GetLinkerLanguage(configName)); @@ -647,10 +648,11 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( target->GetName().c_str()); return; } - if (linkLanguage == "C" || linkLanguage == "CXX" || - linkLanguage == "Fortran") { + langForClCompile = linkLanguage; + if (langForClCompile == "C" || langForClCompile == "CXX" || + langForClCompile == "Fortran") { std::string baseFlagVar = "CMAKE_"; - baseFlagVar += linkLanguage; + baseFlagVar += langForClCompile; baseFlagVar += "_FLAGS"; flags = this->Makefile->GetRequiredDefinition(baseFlagVar.c_str()); std::string flagVar = @@ -667,7 +669,7 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } // Add the target-specific flags. - this->AddCompileOptions(flags, target, linkLanguage, configName); + this->AddCompileOptions(flags, target, langForClCompile, configName); // Check IPO related warning/error. target->IsIPOEnabled(linkLanguage, configName); @@ -703,7 +705,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( targetOptions.Parse(defineFlags.c_str()); targetOptions.ParseFinish(); std::vector<std::string> targetDefines; - target->GetCompileDefinitions(targetDefines, configName, "CXX"); + if (!langForClCompile.empty()) { + target->GetCompileDefinitions(targetDefines, configName, langForClCompile); + } targetOptions.AddDefines(targetDefines); targetOptions.SetVerboseMakefile( this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")); @@ -792,10 +796,13 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( << "\\$(ConfigurationName)\"\n"; } fout << "\t\t\t\tAdditionalIncludeDirectories=\""; - std::vector<std::string> includes; - this->GetIncludeDirectories(includes, target, "C", configName); - std::vector<std::string>::iterator i = includes.begin(); - for (; i != includes.end(); ++i) { + std::vector<std::string> includes_cl; + if (!langForClCompile.empty()) { + this->GetIncludeDirectories(includes_cl, target, langForClCompile, + configName); + } + std::vector<std::string>::iterator i = includes_cl.begin(); + for (; i != includes_cl.end(); ++i) { // output the include path std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); fout << ipath << ";"; @@ -810,7 +817,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } fout << "\"\n"; targetOptions.OutputFlagMap(fout, "\t\t\t\t"); - targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", "CXX"); + targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", + langForClCompile); fout << "\t\t\t\tObjectFile=\"$(IntDir)\\\"\n"; if (target->GetType() <= cmStateEnums::OBJECT_LIBRARY) { // Specify the compiler program database file if configured. @@ -829,9 +837,12 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( "\t\t\t\tName=\"MASM\"\n" "\t\t\t\tIncludePaths=\"" ; + std::vector<std::string> includes_masm; + this->GetIncludeDirectories(includes_masm, target, "ASM_MASM", + configName); /* clang-format on */ const char* sep = ""; - for (i = includes.begin(); i != includes.end(); ++i) { + for (i = includes_masm.begin(); i != includes_masm.end(); ++i) { std::string inc = *i; cmConvertToWindowsSlash(inc); fout << sep << this->EscapeForXML(inc); @@ -859,7 +870,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n" << "\t\t\t\tAdditionalIncludeDirectories=\""; - for (i = includes.begin(); i != includes.end(); ++i) { + std::vector<std::string> includes_rc; + this->GetIncludeDirectories(includes_rc, target, "RC", configName); + for (i = includes_rc.begin(); i != includes_rc.end(); ++i) { std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); fout << ipath << ";"; } @@ -873,7 +886,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"; fout << "\t\t\t\tAdditionalIncludeDirectories=\""; - for (i = includes.begin(); i != includes.end(); ++i) { + std::vector<std::string> includes_midl; + this->GetIncludeDirectories(includes_midl, target, "MIDL", configName); + for (i = includes_midl.begin(); i != includes_midl.end(); ++i) { std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); fout << ipath << ";"; } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index b42495c..eeeb54f 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1965,7 +1965,7 @@ cmSourceGroup* cmMakefile::GetSourceGroup( if (sg != nullptr) { // iterate through its children to find match source group for (unsigned int i = 1; i < name.size(); ++i) { - sg = sg->LookupChild(name[i].c_str()); + sg = sg->LookupChild(name[i]); if (sg == nullptr) { break; } @@ -2009,7 +2009,7 @@ void cmMakefile::AddSourceGroup(const std::vector<std::string>& name, if (i == -1) { // group does not exist nor belong to any existing group // add its first component - this->SourceGroups.push_back(cmSourceGroup(name[0].c_str(), regex)); + this->SourceGroups.push_back(cmSourceGroup(name[0], regex)); sg = this->GetSourceGroup(currentName); i = 0; // last component found } @@ -2019,9 +2019,8 @@ void cmMakefile::AddSourceGroup(const std::vector<std::string>& name, } // build the whole source group path for (++i; i <= lastElement; ++i) { - sg->AddChild( - cmSourceGroup(name[i].c_str(), nullptr, sg->GetFullName().c_str())); - sg = sg->LookupChild(name[i].c_str()); + sg->AddChild(cmSourceGroup(name[i], nullptr, sg->GetFullName().c_str())); + sg = sg->LookupChild(name[i]); } sg->SetGroupRegex(regex); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index e0cc35a..594e0f5 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -760,7 +760,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, orderOnlyDeps, vars, rspfile, commandLineLengthLimit, &usedResponseFile); - this->WriteDeviceLinkRule(usedResponseFile); + this->WriteDeviceLinkRule(false); } void cmNinjaNormalTargetGenerator::WriteLinkStatement() diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 255a532..18ecbe7 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -2,16 +2,13 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGen.h" #include "cmAlgorithms.h" -#include "cmProcessOutput.h" #include "cmSystemTools.h" -#include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <iterator> #include <sstream> -#include <stddef.h> // - Static variables @@ -21,8 +18,8 @@ std::string const genNameUic = "AutoUic"; std::string const genNameRcc = "AutoRcc"; std::string const mcNameSingle = "SINGLE"; -std::string const mcNameWrap = "WRAP"; -std::string const mcNameFull = "FULL"; +std::string const mcNameWrapper = "WRAPPER"; +std::string const mcNameMulti = "MULTI"; // - Static functions @@ -80,203 +77,53 @@ void MergeOptions(std::vector<std::string>& baseOpts, baseOpts.insert(baseOpts.end(), extraOpts.begin(), extraOpts.end()); } -/// @brief Reads the resource files list from from a .qrc file - Qt4 version -/// @return True if the .qrc file was successfully parsed -static bool RccListInputsQt4(std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage) -{ - bool allGood = true; - // Read qrc file content into string - std::string qrcContents; - { - cmsys::ifstream ifs(fileName.c_str()); - if (ifs) { - std::ostringstream osst; - osst << ifs.rdbuf(); - qrcContents = osst.str(); - } else { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc file not readable:\n "; - err += cmQtAutoGen::Quoted(fileName); - err += "\n"; - } - allGood = false; - } - } - if (allGood) { - // qrc file directory - std::string qrcDir(cmSystemTools::GetFilenamePath(fileName)); - if (!qrcDir.empty()) { - qrcDir += '/'; - } - - cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); - cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); - - size_t offset = 0; - while (fileMatchRegex.find(qrcContents.c_str() + offset)) { - std::string qrcEntry = fileMatchRegex.match(1); - offset += qrcEntry.size(); - { - fileReplaceRegex.find(qrcEntry); - std::string tag = fileReplaceRegex.match(1); - qrcEntry = qrcEntry.substr(tag.size()); - } - if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) { - qrcEntry = qrcDir + qrcEntry; - } - files.push_back(qrcEntry); - } - } - return allGood; -} - -/// @brief Reads the resource files list from from a .qrc file - Qt5 version -/// @return True if the .qrc file was successfully parsed -static bool RccListInputsQt5(std::string const& rccCommand, - std::vector<std::string> const& rccListOptions, - std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage) -{ - if (rccCommand.empty()) { - cmSystemTools::Error("rcc executable not available"); - return false; - } - - std::string const fileDir = cmSystemTools::GetFilenamePath(fileName); - std::string const fileNameName = cmSystemTools::GetFilenameName(fileName); - - // Run rcc list command - bool result = false; - int retVal = 0; - std::string rccStdOut; - std::string rccStdErr; - { - std::vector<std::string> command; - command.push_back(rccCommand); - command.insert(command.end(), rccListOptions.begin(), - rccListOptions.end()); - command.push_back(fileNameName); - result = cmSystemTools::RunSingleCommand( - command, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), - cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto); - } - if (!result || retVal) { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc list process failed for:\n "; - err += cmQtAutoGen::Quoted(fileName); - err += "\n"; - err += rccStdOut; - err += "\n"; - err += rccStdErr; - err += "\n"; - } - return false; - } - - // Lambda to strip CR characters - auto StripCR = [](std::string& line) { - std::string::size_type cr = line.find('\r'); - if (cr != std::string::npos) { - line = line.substr(0, cr); - } - }; - - // Parse rcc std output - { - std::istringstream ostr(rccStdOut); - std::string oline; - while (std::getline(ostr, oline)) { - StripCR(oline); - if (!oline.empty()) { - files.push_back(oline); - } - } - } - // Parse rcc error output - { - std::istringstream estr(rccStdErr); - std::string eline; - while (std::getline(estr, eline)) { - StripCR(eline); - if (cmHasLiteralPrefix(eline, "RCC: Error in")) { - static std::string searchString = "Cannot find file '"; - - std::string::size_type pos = eline.find(searchString); - if (pos == std::string::npos) { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc lists unparsable output:\n"; - err += cmQtAutoGen::Quoted(eline); - err += "\n"; - } - return false; - } - pos += searchString.length(); - std::string::size_type sz = eline.size() - pos - 1; - files.push_back(eline.substr(pos, sz)); - } - } - } - - // Convert relative paths to absolute paths - for (std::string& resFile : files) { - resFile = cmSystemTools::CollapseCombinedPath(fileDir, resFile); - } - - return true; -} - // - Class definitions -std::string const cmQtAutoGen::listSep = "<<<S>>>"; +std::string const cmQtAutoGen::ListSep = "<<<S>>>"; +unsigned int const cmQtAutoGen::ParallelMax = 64; -std::string const& cmQtAutoGen::GeneratorName(Generator type) +std::string const& cmQtAutoGen::GeneratorName(GeneratorT type) { switch (type) { - case Generator::GEN: + case GeneratorT::GEN: return genNameGen; - case Generator::MOC: + case GeneratorT::MOC: return genNameMoc; - case Generator::UIC: + case GeneratorT::UIC: return genNameUic; - case Generator::RCC: + case GeneratorT::RCC: return genNameRcc; } return genNameGen; } -std::string cmQtAutoGen::GeneratorNameUpper(Generator genType) +std::string cmQtAutoGen::GeneratorNameUpper(GeneratorT genType) { return cmSystemTools::UpperCase(cmQtAutoGen::GeneratorName(genType)); } -std::string const& cmQtAutoGen::MultiConfigName(MultiConfig config) +std::string const& cmQtAutoGen::MultiConfigName(MultiConfigT config) { switch (config) { - case MultiConfig::SINGLE: + case MultiConfigT::SINGLE: return mcNameSingle; - case MultiConfig::WRAP: - return mcNameWrap; - case MultiConfig::FULL: - return mcNameFull; + case MultiConfigT::WRAPPER: + return mcNameWrapper; + case MultiConfigT::MULTI: + return mcNameMulti; } - return mcNameWrap; + return mcNameWrapper; } -cmQtAutoGen::MultiConfig cmQtAutoGen::MultiConfigType(std::string const& name) +cmQtAutoGen::MultiConfigT cmQtAutoGen::MultiConfigType(std::string const& name) { if (name == mcNameSingle) { - return MultiConfig::SINGLE; + return MultiConfigT::SINGLE; } - if (name == mcNameFull) { - return MultiConfig::FULL; + if (name == mcNameMulti) { + return MultiConfigT::MULTI; } - return MultiConfig::WRAP; + return MultiConfigT::WRAPPER; } std::string cmQtAutoGen::Quoted(std::string const& text) @@ -294,6 +141,33 @@ std::string cmQtAutoGen::Quoted(std::string const& text) return res; } +std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command) +{ + std::string res; + for (std::string const& item : command) { + if (!res.empty()) { + res.push_back(' '); + } + std::string const cesc = cmQtAutoGen::Quoted(item); + if (item.empty() || (cesc.size() > (item.size() + 2)) || + (cesc.find(' ') != std::string::npos)) { + res += cesc; + } else { + res += item; + } + } + return res; +} + +std::string cmQtAutoGen::SubDirPrefix(std::string const& filename) +{ + std::string res(cmSystemTools::GetFilenamePath(filename)); + if (!res.empty()) { + res += '/'; + } + return res; +} + std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename, std::string const& suffix) { @@ -333,27 +207,79 @@ void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts, MergeOptions(baseOpts, newOpts, valueOpts, isQt5); } -bool cmQtAutoGen::RccListInputs(std::string const& rccCommand, - std::vector<std::string> const& rccListOptions, - std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage) +void cmQtAutoGen::RccListParseContent(std::string const& content, + std::vector<std::string>& files) { - bool allGood = false; - if (cmSystemTools::FileExists(fileName.c_str())) { - if (rccListOptions.empty()) { - allGood = RccListInputsQt4(fileName, files, errorMessage); - } else { - allGood = RccListInputsQt5(rccCommand, rccListOptions, fileName, files, - errorMessage); + cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); + cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); + + const char* contentChars = content.c_str(); + while (fileMatchRegex.find(contentChars)) { + std::string const qrcEntry = fileMatchRegex.match(1); + contentChars += qrcEntry.size(); + { + fileReplaceRegex.find(qrcEntry); + std::string const tag = fileReplaceRegex.match(1); + files.push_back(qrcEntry.substr(tag.size())); } - } else { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc resource file does not exist:\n "; - err += cmQtAutoGen::Quoted(fileName); - err += "\n"; + } +} + +bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut, + std::string const& rccStdErr, + std::vector<std::string>& files, + std::string& error) +{ + // Lambda to strip CR characters + auto StripCR = [](std::string& line) { + std::string::size_type cr = line.find('\r'); + if (cr != std::string::npos) { + line = line.substr(0, cr); } + }; + + // Parse rcc std output + { + std::istringstream ostr(rccStdOut); + std::string oline; + while (std::getline(ostr, oline)) { + StripCR(oline); + if (!oline.empty()) { + files.push_back(oline); + } + } + } + // Parse rcc error output + { + std::istringstream estr(rccStdErr); + std::string eline; + while (std::getline(estr, eline)) { + StripCR(eline); + if (cmHasLiteralPrefix(eline, "RCC: Error in")) { + static std::string const searchString = "Cannot find file '"; + + std::string::size_type pos = eline.find(searchString); + if (pos == std::string::npos) { + error = "rcc lists unparsable output:\n"; + error += cmQtAutoGen::Quoted(eline); + error += "\n"; + return false; + } + pos += searchString.length(); + std::string::size_type sz = eline.size() - pos - 1; + files.push_back(eline.substr(pos, sz)); + } + } + } + + return true; +} + +void cmQtAutoGen::RccListConvertFullPath(std::string const& qrcFileDir, + std::vector<std::string>& files) +{ + for (std::string& entry : files) { + std::string tmp = cmSystemTools::CollapseCombinedPath(qrcFileDir, entry); + entry = std::move(tmp); } - return allGood; } diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index e769e93..30ce0f6 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -9,14 +9,18 @@ #include <vector> /** \class cmQtAutoGen - * \brief Class used as namespace for QtAutogen related types and functions + * \brief Common base class for QtAutoGen classes */ class cmQtAutoGen { public: - static std::string const listSep; + /// @brief Nested lists separator + static std::string const ListSep; + /// @brief Maximum number of parallel threads/processes in a generator + static unsigned int const ParallelMax; - enum Generator + /// @brief AutoGen generator type + enum class GeneratorT { GEN, // General MOC, @@ -24,27 +28,33 @@ public: RCC }; - enum MultiConfig + /// @brief Multiconfiguration type + enum class MultiConfigT { - SINGLE, // Single configuration - WRAP, // Multi configuration using wrapper files - FULL // Full multi configuration using per config sources + SINGLE, // Single configuration + WRAPPER, // Multi configuration using wrapper files + MULTI // Multi configuration using per config sources }; public: /// @brief Returns the generator name - static std::string const& GeneratorName(Generator genType); + static std::string const& GeneratorName(GeneratorT genType); /// @brief Returns the generator name in upper case - static std::string GeneratorNameUpper(Generator genType); + static std::string GeneratorNameUpper(GeneratorT genType); /// @brief Returns the multi configuration name string - static std::string const& MultiConfigName(MultiConfig config); + static std::string const& MultiConfigName(MultiConfigT config); /// @brief Returns the multi configuration type - static MultiConfig MultiConfigType(std::string const& name); + static MultiConfigT MultiConfigType(std::string const& name); /// @brief Returns a the string escaped and enclosed in quotes static std::string Quoted(std::string const& text); + static std::string QuotedCommand(std::vector<std::string> const& command); + + /// @brief Returns the parent directory of the file with a "/" suffix + static std::string SubDirPrefix(std::string const& filename); + /// @brief Appends the suffix to the filename before the last dot static std::string AppendFilenameSuffix(std::string const& filename, std::string const& suffix); @@ -59,14 +69,21 @@ public: std::vector<std::string> const& newOpts, bool isQt5); - /// @brief Reads the resource files list from from a .qrc file - /// @arg fileName Must be the absolute path of the .qrc file - /// @return True if the rcc file was successfully read - static bool RccListInputs(std::string const& rccCommand, - std::vector<std::string> const& rccListOptions, - std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage = nullptr); + /// @brief Parses the content of a qrc file + /// + /// Use when rcc does not support the "--list" option + static void RccListParseContent(std::string const& content, + std::vector<std::string>& files); + + /// @brief Parses the output of the "rcc --list ..." command + static bool RccListParseOutput(std::string const& rccStdOut, + std::string const& rccStdErr, + std::vector<std::string>& files, + std::string& error); + + /// @brief Converts relative qrc entry paths to full paths + static void RccListConvertFullPath(std::string const& qrcFileDir, + std::vector<std::string>& files); }; #endif diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index de0ba4f..0b67981 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -1,7 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGen.h" -#include "cmQtAutoGeneratorInitializer.h" +#include "cmQtAutoGenInitializer.h" #include "cmAlgorithms.h" #include "cmCustomCommand.h" @@ -24,6 +24,7 @@ #include "cm_sys_stat.h" #include "cmake.h" #include "cmsys/FStream.hxx" +#include "cmsys/SystemInformation.hxx" #include <algorithm> #include <array> @@ -52,6 +53,20 @@ inline static std::string GetSafeProperty(cmSourceFile const* sf, return std::string(SafeString(sf->GetProperty(key))); } +static std::size_t GetParallelCPUCount() +{ + static std::size_t count = 0; + // Detect only on the first call + if (count == 0) { + cmsys::SystemInformation info; + info.RunCPUCheck(); + count = info.GetNumberOfPhysicalCPU(); + count = std::max<std::size_t>(count, 1); + count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax); + } + return count; +} + static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, std::string const& value) { @@ -85,12 +100,12 @@ static void AddDefinitionEscaped( seplist.push_back(std::move(blist)); } makefile->AddDefinition(key, cmOutputConverter::EscapeForCMake( - cmJoin(seplist, cmQtAutoGen::listSep)) + cmJoin(seplist, cmQtAutoGen::ListSep)) .c_str()); } static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName, - cmQtAutoGen::Generator genType) + cmQtAutoGen::GeneratorT genType) { cmSourceGroup* sourceGroup = nullptr; // Acquire source group @@ -101,10 +116,10 @@ static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName, std::array<std::string, 2> props; // Use generator specific group name switch (genType) { - case cmQtAutoGen::MOC: + case cmQtAutoGen::GeneratorT::MOC: props[0] = "AUTOMOC_SOURCE_GROUP"; break; - case cmQtAutoGen::RCC: + case cmQtAutoGen::GeneratorT::RCC: props[0] = "AUTORCC_SOURCE_GROUP"; break; default: @@ -211,7 +226,7 @@ static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, return cycle; } -cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer( +cmQtAutoGenInitializer::cmQtAutoGenInitializer( cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled, std::string const& qtVersionMajor) : Target(target) @@ -219,13 +234,13 @@ cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer( , UicEnabled(uicEnabled) , RccEnabled(rccEnabled) , QtVersionMajor(qtVersionMajor) - , MultiConfig(cmQtAutoGen::WRAP) + , MultiConfig(MultiConfigT::WRAPPER) { - this->QtVersionMinor = cmQtAutoGeneratorInitializer::GetQtMinorVersion( - target, this->QtVersionMajor); + this->QtVersionMinor = + cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor); } -void cmQtAutoGeneratorInitializer::InitCustomTargets() +void cmQtAutoGenInitializer::InitCustomTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); @@ -240,19 +255,19 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Multi configuration { if (!globalGen->IsMultiConfig()) { - this->MultiConfig = cmQtAutoGen::SINGLE; + this->MultiConfig = MultiConfigT::SINGLE; } // FIXME: Xcode does not support per-config sources, yet. // (EXCLUDED_SOURCE_FILE_NAMES) // if (globalGen->GetName().find("Xcode") != std::string::npos) { - // return cmQtAutoGen::FULL; + // return MultiConfigT::MULTI; //} // FIXME: Visual Studio does not support per-config sources, yet. // (EXCLUDED_SOURCE_FILE_NAMES) // if (globalGen->GetName().find("Visual Studio") != std::string::npos) { - // return cmQtAutoGen::FULL; + // return MultiConfigT::MULTI; //} } @@ -294,7 +309,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() this->AutogenInfoFile += "/AutogenInfo.cmake"; this->AutogenSettingsFile = this->DirInfo; - this->AutogenSettingsFile += "/AutogenOldSettings.cmake"; + this->AutogenSettingsFile += "/AutogenOldSettings.txt"; } // Autogen target FOLDER property @@ -324,7 +339,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() { std::string base = this->DirInfo; base += "/AutogenOldSettings"; - if (this->MultiConfig == cmQtAutoGen::SINGLE) { + if (this->MultiConfig == MultiConfigT::SINGLE) { AddCleanFile(makefile, base.append(".cmake")); } else { for (std::string const& cfg : this->ConfigsList) { @@ -340,7 +355,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Add moc compilation to generated files list if (this->MocEnabled) { std::string const mocsComp = this->DirBuild + "/mocs_compilation.cpp"; - auto files = this->AddGeneratedSource(mocsComp, cmQtAutoGen::MOC); + auto files = this->AddGeneratedSource(mocsComp, GeneratorT::MOC); for (std::string& file : files) { autogenProvides.push_back(std::move(file)); } @@ -349,7 +364,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES if (this->MocEnabled || this->UicEnabled) { std::string includeDir = this->DirBuild + "/include"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { + if (this->MultiConfig != MultiConfigT::SINGLE) { includeDir += "_$<CONFIG>"; } this->Target->AddIncludeDirectory(includeDir, true); @@ -560,10 +575,10 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() msg += "For compatibility, CMake is excluding the GENERATED source " "file(s):\n"; for (const std::string& absFile : generatedHeaders) { - msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n"); + msg.append(" ").append(Quoted(absFile)).append("\n"); } for (const std::string& absFile : generatedSources) { - msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n"); + msg.append(" ").append(Quoted(absFile)).append("\n"); } msg += "from processing by "; msg += tools; @@ -630,7 +645,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() qrc.InfoFile = base; qrc.InfoFile += "Info.cmake"; qrc.SettingsFile = base; - qrc.SettingsFile += "Settings.cmake"; + qrc.SettingsFile += "Settings.txt"; } } } @@ -650,16 +665,16 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() std::vector<std::string> nameOpts; nameOpts.emplace_back("-name"); nameOpts.emplace_back(std::move(name)); - cmQtAutoGen::RccMergeOptions(opts, nameOpts, QtV5); + RccMergeOptions(opts, nameOpts, QtV5); } // Merge file option - cmQtAutoGen::RccMergeOptions(opts, qrc.Options, QtV5); + RccMergeOptions(opts, qrc.Options, QtV5); qrc.Options = std::move(opts); } for (Qrc& qrc : this->Qrcs) { // Register file at target std::vector<std::string> const ccOutput = - this->AddGeneratedSource(qrc.RccFile, cmQtAutoGen::RCC); + this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC); cmCustomCommandLines commandLines; { @@ -686,8 +701,9 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() ccName += qrc.PathChecksum; } std::vector<std::string> ccDepends; - // Add the .qrc file to the custom target dependencies + // Add the .qrc and info file to the custom target dependencies ccDepends.push_back(qrc.QrcFile); + ccDepends.push_back(qrc.InfoFile); cmTarget* autoRccTarget = makefile->AddUtilityCommand( ccName, cmMakefile::TargetOrigin::Generator, true, @@ -709,15 +725,14 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() { std::vector<std::string> ccByproducts; std::vector<std::string> ccDepends; - // Add the .qrc file to the custom command dependencies + // Add the .qrc and info file to the custom command dependencies ccDepends.push_back(qrc.QrcFile); + ccDepends.push_back(qrc.InfoFile); // Add the resource files to the dependencies { std::string error; - if (cmQtAutoGen::RccListInputs(this->RccExecutable, - this->RccListOptions, qrc.QrcFile, - qrc.Resources, &error)) { + if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) { for (std::string const& fileName : qrc.Resources) { // Add resource file to the custom command dependencies ccDepends.push_back(fileName); @@ -885,7 +900,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() } } -void cmQtAutoGeneratorInitializer::SetupCustomTargets() +void cmQtAutoGenInitializer::SetupCustomTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); @@ -903,8 +918,16 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() // Basic setup AddDefinitionEscaped(makefile, "_multi_config", - cmQtAutoGen::MultiConfigName(this->MultiConfig)); + MultiConfigName(this->MultiConfig)); AddDefinitionEscaped(makefile, "_build_dir", this->DirBuild); + { + std::string parallel = GetSafeProperty(this->Target, "AUTOGEN_PARALLEL"); + // Autodetect number of CPUs + if (parallel.empty() || (parallel == "AUTO")) { + parallel = std::to_string(GetParallelCPUCount()); + } + AddDefinitionEscaped(makefile, "_parallel", parallel); + } if (this->MocEnabled || this->UicEnabled) { AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor); @@ -929,7 +952,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() // Create info directory on demand if (!cmSystemTools::MakeDirectory(this->DirInfo)) { std::string emsg = ("Could not create directory: "); - emsg += cmQtAutoGen::Quoted(this->DirInfo); + emsg += Quoted(this->DirInfo); cmSystemTools::Error(emsg.c_str()); } @@ -951,7 +974,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() if (!ofs) { // File open error std::string error = "Internal CMake error when trying to open file: "; - error += cmQtAutoGen::Quoted(fileName); + error += Quoted(fileName); error += " for writing."; cmSystemTools::Error(error.c_str()); } @@ -984,11 +1007,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() OfsWriteMap("AM_MOC_INCLUDES", this->ConfigMocIncludes); OfsWriteMap("AM_UIC_TARGET_OPTIONS", this->ConfigUicOptions); // Settings files (only require for multi configuration generators) - if (this->MultiConfig != cmQtAutoGen::SINGLE) { + if (this->MultiConfig != MultiConfigT::SINGLE) { std::map<std::string, std::string> settingsFiles; for (std::string const& cfg : this->ConfigsList) { - settingsFiles[cfg] = cmQtAutoGen::AppendFilenameSuffix( - this->AutogenSettingsFile, "_" + cfg); + settingsFiles[cfg] = + AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg); } OfsWriteMap("AM_SETTINGS_FILE", settingsFiles); } @@ -1033,11 +1056,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes); // Settings files (only require for multi configuration generators) - if (this->MultiConfig != cmQtAutoGen::SINGLE) { + if (this->MultiConfig != MultiConfigT::SINGLE) { std::map<std::string, std::string> settingsFiles; for (std::string const& cfg : this->ConfigsList) { settingsFiles[cfg] = - cmQtAutoGen::AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); + AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); } OfsWriteMap("ARCC_SETTINGS_FILE", settingsFiles); } @@ -1049,7 +1072,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() } } -void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc() +void cmQtAutoGenInitializer::SetupCustomTargetsMoc() { cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); cmMakefile* makefile = this->Target->Target->GetMakefile(); @@ -1148,7 +1171,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc() } } -void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic() +void cmQtAutoGenInitializer::SetupCustomTargetsUic() { cmMakefile* makefile = this->Target->Target->GetMakefile(); @@ -1266,17 +1289,16 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic() } } -std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource( - std::string const& filename, cmQtAutoGen::Generator genType) +std::vector<std::string> cmQtAutoGenInitializer::AddGeneratedSource( + std::string const& filename, GeneratorT genType) { std::vector<std::string> genFiles; // Register source file in makefile and source group - if (this->MultiConfig != cmQtAutoGen::FULL) { + if (this->MultiConfig != MultiConfigT::MULTI) { genFiles.push_back(filename); } else { for (std::string const& cfg : this->ConfigsList) { - genFiles.push_back( - cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg)); + genFiles.push_back(AppendFilenameSuffix(filename, "_" + cfg)); } } { @@ -1292,14 +1314,14 @@ std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource( } // Add source file to target - if (this->MultiConfig != cmQtAutoGen::FULL) { + if (this->MultiConfig != MultiConfigT::MULTI) { this->Target->AddSource(filename); } else { for (std::string const& cfg : this->ConfigsList) { std::string src = "$<$<CONFIG:"; src += cfg; src += ">:"; - src += cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg); + src += AppendFilenameSuffix(filename, "_" + cfg); src += ">"; this->Target->AddSource(src); } @@ -1308,7 +1330,7 @@ std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource( return genFiles; } -std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion( +std::string cmQtAutoGenInitializer::GetQtMajorVersion( cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); @@ -1324,7 +1346,7 @@ std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion( return qtMajor; } -std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion( +std::string cmQtAutoGenInitializer::GetQtMinorVersion( cmGeneratorTarget const* target, std::string const& qtVersionMajor) { cmMakefile* makefile = target->Target->GetMakefile(); @@ -1344,7 +1366,7 @@ std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion( return qtMinor; } -bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual( +bool cmQtAutoGenInitializer::QtVersionGreaterOrEqual( unsigned long requestMajor, unsigned long requestMinor) const { unsigned long majorUL(0); @@ -1356,3 +1378,84 @@ bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual( } return false; } + +/// @brief Reads the resource files list from from a .qrc file +/// @arg fileName Must be the absolute path of the .qrc file +/// @return True if the rcc file was successfully read +bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName, + std::vector<std::string>& files, + std::string& error) +{ + if (!cmSystemTools::FileExists(fileName.c_str())) { + error = "rcc resource file does not exist:\n "; + error += Quoted(fileName); + error += "\n"; + return false; + } + if (!RccListOptions.empty()) { + // Use rcc for file listing + if (RccExecutable.empty()) { + error = "rcc executable not available"; + return false; + } + + // Run rcc list command in the directory of the qrc file with the pathless + // qrc file name argument. This way rcc prints relative paths. + // This avoids issues on Windows when the qrc file is in a path that + // contains non-ASCII characters. + std::string const fileDir = cmSystemTools::GetFilenamePath(fileName); + std::string const fileNameName = cmSystemTools::GetFilenameName(fileName); + + bool result = false; + int retVal = 0; + std::string rccStdOut; + std::string rccStdErr; + { + std::vector<std::string> cmd; + cmd.push_back(RccExecutable); + cmd.insert(cmd.end(), RccListOptions.begin(), RccListOptions.end()); + cmd.push_back(fileNameName); + result = cmSystemTools::RunSingleCommand( + cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), + cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto); + } + if (!result || retVal) { + error = "rcc list process failed for:\n "; + error += Quoted(fileName); + error += "\n"; + error += rccStdOut; + error += "\n"; + error += rccStdErr; + error += "\n"; + return false; + } + if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) { + return false; + } + } else { + // We can't use rcc for the file listing. + // Read the qrc file content into string and parse it. + { + std::string qrcContents; + { + cmsys::ifstream ifs(fileName.c_str()); + if (ifs) { + std::ostringstream osst; + osst << ifs.rdbuf(); + qrcContents = osst.str(); + } else { + error = "rcc file not readable:\n "; + error += Quoted(fileName); + error += "\n"; + return false; + } + } + // Parse string content + RccListParseContent(qrcContents, files); + } + } + + // Convert relative paths to absolute paths + RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files); + return true; +} diff --git a/Source/cmQtAutoGeneratorInitializer.h b/Source/cmQtAutoGenInitializer.h index e06e1c4..a667017 100644 --- a/Source/cmQtAutoGeneratorInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -1,7 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGeneratorInitializer_h -#define cmQtAutoGeneratorInitializer_h +#ifndef cmQtAutoGenInitializer_h +#define cmQtAutoGenInitializer_h #include "cmConfigure.h" // IWYU pragma: keep #include "cmQtAutoGen.h" @@ -13,7 +13,8 @@ class cmGeneratorTarget; -class cmQtAutoGeneratorInitializer +/// @brief Initializes the QtAutoGen generators +class cmQtAutoGenInitializer : public cmQtAutoGen { public: static std::string GetQtMajorVersion(cmGeneratorTarget const* target); @@ -43,9 +44,9 @@ public: }; public: - cmQtAutoGeneratorInitializer(cmGeneratorTarget* target, bool mocEnabled, - bool uicEnabled, bool rccEnabled, - std::string const& qtVersionMajor); + cmQtAutoGenInitializer(cmGeneratorTarget* target, bool mocEnabled, + bool uicEnabled, bool rccEnabled, + std::string const& qtVersionMajor); void InitCustomTargets(); void SetupCustomTargets(); @@ -55,11 +56,15 @@ private: void SetupCustomTargetsUic(); std::vector<std::string> AddGeneratedSource(std::string const& filename, - cmQtAutoGen::Generator genType); + GeneratorT genType); bool QtVersionGreaterOrEqual(unsigned long requestMajor, unsigned long requestMinor) const; + bool RccListInputs(std::string const& fileName, + std::vector<std::string>& files, + std::string& errorMessage); + private: cmGeneratorTarget* Target; bool MocEnabled; @@ -73,7 +78,7 @@ private: // Configurations std::string ConfigDefault; std::vector<std::string> ConfigsList; - cmQtAutoGen::MultiConfig MultiConfig; + MultiConfigT MultiConfig; // Names std::string AutogenTargetName; std::string AutogenFolder; diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index ee0ddbc..5b2b6d0 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -4,7 +4,6 @@ #include "cmQtAutoGenerator.h" #include "cmsys/FStream.hxx" -#include "cmsys/Terminal.h" #include "cmAlgorithms.h" #include "cmGlobalGenerator.h" @@ -14,108 +13,55 @@ #include "cmSystemTools.h" #include "cmake.h" -// -- Static functions - -static std::string HeadLine(std::string const& title) -{ - std::string head = title; - head += '\n'; - head.append(head.size() - 1, '-'); - head += '\n'; - return head; -} - -static std::string QuotedCommand(std::vector<std::string> const& command) -{ - std::string res; - for (std::string const& item : command) { - if (!res.empty()) { - res.push_back(' '); - } - std::string const cesc = cmQtAutoGen::Quoted(item); - if (item.empty() || (cesc.size() > (item.size() + 2)) || - (cesc.find(' ') != std::string::npos)) { - res += cesc; - } else { - res += item; - } - } - return res; -} +#include <algorithm> // -- Class methods -cmQtAutoGenerator::cmQtAutoGenerator() - : Verbose(cmSystemTools::HasEnv("VERBOSE")) - , ColorOutput(true) +void cmQtAutoGenerator::Logger::SetVerbose(bool value) { - { - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); - } - } + Verbose_ = value; } -bool cmQtAutoGenerator::Run(std::string const& infoFile, - std::string const& config) +void cmQtAutoGenerator::Logger::SetColorOutput(bool value) { - // Info settings - this->InfoFile = infoFile; - cmSystemTools::ConvertToUnixSlashes(this->InfoFile); - this->InfoDir = cmSystemTools::GetFilenamePath(infoFile); - this->InfoConfig = config; - - cmake cm(cmake::RoleScript); - cm.SetHomeOutputDirectory(this->InfoDir); - cm.SetHomeDirectory(this->InfoDir); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(this->InfoDir); - snapshot.GetDirectory().SetCurrentSource(this->InfoDir); - - auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); - // The OLD/WARN behavior for policy CMP0053 caused a speed regression. - // https://gitlab.kitware.com/cmake/cmake/issues/17570 - makefile->SetPolicyVersion("3.9"); - gg.SetCurrentMakefile(makefile.get()); - - return this->Process(makefile.get()); + ColorOutput_ = value; } -void cmQtAutoGenerator::LogBold(std::string const& message) const +std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title) { - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, this->ColorOutput); + std::string head = title; + head += '\n'; + head.append(head.size() - 1, '-'); + head += '\n'; + return head; } -void cmQtAutoGenerator::LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const +void cmQtAutoGenerator::Logger::Info(GeneratorT genType, + std::string const& message) { - std::string msg = cmQtAutoGen::GeneratorName(genType); + std::string msg = GeneratorName(genType); msg += ": "; msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } - cmSystemTools::Stdout(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stdout(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const +void cmQtAutoGenerator::Logger::Warning(GeneratorT genType, + std::string const& message) { - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += " warning:"; + std::string msg; if (message.find('\n') == std::string::npos) { // Single line message - msg.push_back(' '); + msg += GeneratorName(genType); + msg += " warning: "; } else { // Multi line message - msg.push_back('\n'); + msg += HeadLine(GeneratorName(genType) + " warning"); } // Message msg += message; @@ -123,55 +69,60 @@ void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType, msg.push_back('\n'); } msg.push_back('\n'); - cmSystemTools::Stdout(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stdout(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerator::LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const +void cmQtAutoGenerator::Logger::WarningFile(GeneratorT genType, + std::string const& filename, + std::string const& message) { std::string msg = " "; - msg += cmQtAutoGen::Quoted(filename); + msg += Quoted(filename); msg.push_back('\n'); // Message msg += message; - this->LogWarning(genType, msg); + Warning(genType, msg); } -void cmQtAutoGenerator::LogError(cmQtAutoGen::Generator genType, - std::string const& message) const +void cmQtAutoGenerator::Logger::Error(GeneratorT genType, + std::string const& message) { std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); + msg += HeadLine(GeneratorName(genType) + " error"); // Message msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stderr(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerator::LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const +void cmQtAutoGenerator::Logger::ErrorFile(GeneratorT genType, + std::string const& filename, + std::string const& message) { std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); + emsg += Quoted(filename); emsg += '\n'; // Message emsg += message; - this->LogError(genType, emsg); + Error(genType, emsg); } -void cmQtAutoGenerator::LogCommandError( - cmQtAutoGen::Generator genType, std::string const& message, - std::vector<std::string> const& command, std::string const& output) const +void cmQtAutoGenerator::Logger::ErrorCommand( + GeneratorT genType, std::string const& message, + std::vector<std::string> const& command, std::string const& output) { std::string msg; msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); + msg += HeadLine(GeneratorName(genType) + " subprocess error"); msg += message; if (msg.back() != '\n') { msg.push_back('\n'); @@ -189,135 +140,495 @@ void cmQtAutoGenerator::LogCommandError( msg.push_back('\n'); } msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stderr(msg.c_str(), msg.size()); + } } -/** - * @brief Generates the parent directory of the given file on demand - * @return True on success - */ -bool cmQtAutoGenerator::MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const +std::string cmQtAutoGenerator::FileSystem::RealPath( + std::string const& filename) { - bool success = true; - std::string const dirName = cmSystemTools::GetFilenamePath(filename); - if (!dirName.empty()) { - if (!cmSystemTools::MakeDirectory(dirName)) { - this->LogFileError(genType, filename, - "Could not create parent directory"); - success = false; - } - } - return success; + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::GetRealPath(filename); +} + +bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::FileExists(filename); } -/** - * @brief Tests if buildFile is older than sourceFile - * @return True if buildFile is older than sourceFile. - * False may indicate an error. - */ -bool cmQtAutoGenerator::FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error) +bool cmQtAutoGenerator::FileSystem::FileIsOlderThan( + std::string const& buildFile, std::string const& sourceFile, + std::string* error) { + bool res(false); int result = 0; - if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { - return (result < 0); + { + std::lock_guard<std::mutex> lock(Mutex_); + res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result); } - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(cmQtAutoGen::Quoted(buildFile)); - error->append("\nand\n "); - error->append(cmQtAutoGen::Quoted(sourceFile)); + if (res) { + res = (result < 0); + } else { + if (error != nullptr) { + error->append( + "File modification time comparison failed for the files\n "); + error->append(Quoted(buildFile)); + error->append("\nand\n "); + error->append(Quoted(sourceFile)); + } } - return false; + return res; } -bool cmQtAutoGenerator::FileRead(std::string& content, - std::string const& filename, - std::string* error) +bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content, + std::string const& filename, + std::string* error) { bool success = false; - if (cmSystemTools::FileExists(filename)) { - std::size_t const length = cmSystemTools::FileLength(filename); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - if (ifs) { - content.resize(length); - ifs.read(&content.front(), content.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + if (cmSystemTools::FileExists(filename)) { + std::size_t const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); if (ifs) { - success = true; - } else { - content.clear(); - if (error != nullptr) { - error->append("Reading from the file failed."); + content.resize(length); + ifs.read(&content.front(), content.size()); + if (ifs) { + success = true; + } else { + content.clear(); + if (error != nullptr) { + error->append("Reading from the file failed."); + } } + } else if (error != nullptr) { + error->append("Opening the file for reading failed."); } } else if (error != nullptr) { - error->append("Opening the file for reading failed."); + error->append("The file does not exist."); } - } else if (error != nullptr) { - error->append("The file does not exist."); } return success; } -bool cmQtAutoGenerator::FileWrite(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& content) +bool cmQtAutoGenerator::FileSystem::FileRead(GeneratorT genType, + std::string& content, + std::string const& filename) { std::string error; + if (!FileRead(content, filename, &error)) { + Log()->ErrorFile(genType, filename, error); + return false; + } + return true; +} + +bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename, + std::string const& content, + std::string* error) +{ + bool success = false; // Make sure the parent directory exists - if (this->MakeParentDirectory(genType, filename)) { + if (MakeParentDirectory(filename)) { + std::lock_guard<std::mutex> lock(Mutex_); cmsys::ofstream outfile; outfile.open(filename.c_str(), (std::ios::out | std::ios::binary | std::ios::trunc)); if (outfile) { outfile << content; // Check for write errors - if (!outfile.good()) { - error = "File writing failed"; + if (outfile.good()) { + success = true; + } else { + if (error != nullptr) { + error->assign("File writing failed"); + } } } else { - error = "Opening file for writing failed"; + if (error != nullptr) { + error->assign("Opening file for writing failed"); + } + } + } else { + if (error != nullptr) { + error->assign("Could not create parent directory"); } } - if (!error.empty()) { - this->LogFileError(genType, filename, error); + return success; +} + +bool cmQtAutoGenerator::FileSystem::FileWrite(GeneratorT genType, + std::string const& filename, + std::string const& content) +{ + std::string error; + if (!FileWrite(filename, content, &error)) { + Log()->ErrorFile(genType, filename, error); return false; } return true; } -bool cmQtAutoGenerator::FileDiffers(std::string const& filename, - std::string const& content) +bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename, + std::string const& content) { bool differs = true; { std::string oldContents; - if (this->FileRead(oldContents, filename)) { + if (FileRead(oldContents, filename)) { differs = (oldContents != content); } } return differs; } -/** - * @brief Runs a command and returns true on success - * @return True on success - */ -bool cmQtAutoGenerator::RunCommand(std::vector<std::string> const& command, - std::string& output) const -{ - // Log command - if (this->Verbose) { - std::string qcmd = QuotedCommand(command); - qcmd.push_back('\n'); - cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); - } - // Execute command - int retVal = 0; - bool res = cmSystemTools::RunSingleCommand( - command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); - return (res && (retVal == 0)); +bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::RemoveFile(filename); +} + +bool cmQtAutoGenerator::FileSystem::Touch(std::string const& filename) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::Touch(filename, false); +} + +bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::MakeDirectory(dirname); +} + +bool cmQtAutoGenerator::FileSystem::MakeDirectory(GeneratorT genType, + std::string const& dirname) +{ + if (!MakeDirectory(dirname)) { + Log()->ErrorFile(genType, dirname, "Could not create directory"); + return false; + } + return true; +} + +bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( + std::string const& filename) +{ + bool success = true; + std::string const dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + success = MakeDirectory(dirName); + } + return success; +} + +bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( + GeneratorT genType, std::string const& filename) +{ + if (!MakeParentDirectory(filename)) { + Log()->ErrorFile(genType, filename, "Could not create parent directory"); + return false; + } + return true; +} + +int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop, + ReadOnlyProcessT* process) +{ + Process_ = process; + Target_ = nullptr; + return UVPipe_.init(*uv_loop, 0, this); +} + +int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target) +{ + Target_ = target; + return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData); +} + +void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset() +{ + Process_ = nullptr; + Target_ = nullptr; + UVPipe_.reset(); + Buffer_.clear(); + Buffer_.shrink_to_fit(); +} + +void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle, + size_t suggestedSize, + uv_buf_t* buf) +{ + auto& pipe = *reinterpret_cast<PipeT*>(handle->data); + pipe.Buffer_.resize(suggestedSize); + buf->base = &pipe.Buffer_.front(); + buf->len = pipe.Buffer_.size(); +} + +void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) +{ + auto& pipe = *reinterpret_cast<PipeT*>(stream->data); + if (nread > 0) { + // Append data to merged output + if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) { + pipe.Target_->append(buf->base, nread); + } + } else if (nread < 0) { + // EOF or error + auto* proc = pipe.Process_; + // Check it this an unusual error + if (nread != UV_EOF) { + if (!proc->Result()->error()) { + proc->Result()->ErrorMessage = + "libuv reading from pipe failed with error code "; + proc->Result()->ErrorMessage += std::to_string(nread); + } + } + // Clear libuv pipe handle and try to finish + pipe.reset(); + proc->UVTryFinish(); + } +} + +void cmQtAutoGenerator::ProcessResultT::reset() +{ + ExitStatus = 0; + TermSignal = 0; + if (!StdOut.empty()) { + StdOut.clear(); + StdOut.shrink_to_fit(); + } + if (!StdErr.empty()) { + StdErr.clear(); + StdErr.shrink_to_fit(); + } + if (!ErrorMessage.empty()) { + ErrorMessage.clear(); + ErrorMessage.shrink_to_fit(); + } +} + +void cmQtAutoGenerator::ReadOnlyProcessT::setup( + ProcessResultT* result, bool mergedOutput, + std::vector<std::string> const& command, std::string const& workingDirectory) +{ + Setup_.WorkingDirectory = workingDirectory; + Setup_.Command = command; + Setup_.Result = result; + Setup_.MergedOutput = mergedOutput; +} + +bool cmQtAutoGenerator::ReadOnlyProcessT::start( + uv_loop_t* uv_loop, std::function<void()>&& finishedCallback) +{ + if (IsStarted() || (Result() == nullptr)) { + return false; + } + + // Reset result before the start + Result()->reset(); + + // Fill command string pointers + if (!Setup().Command.empty()) { + CommandPtr_.reserve(Setup().Command.size() + 1); + for (std::string const& arg : Setup().Command) { + CommandPtr_.push_back(arg.c_str()); + } + CommandPtr_.push_back(nullptr); + } else { + Result()->ErrorMessage = "Empty command"; + } + + if (!Result()->error()) { + if (UVPipeOut_.init(uv_loop, this) != 0) { + Result()->ErrorMessage = "libuv stdout pipe initialization failed"; + } + } + if (!Result()->error()) { + if (UVPipeErr_.init(uv_loop, this) != 0) { + Result()->ErrorMessage = "libuv stderr pipe initialization failed"; + } + } + if (!Result()->error()) { + // -- Setup process stdio options + // stdin + UVOptionsStdIO_[0].flags = UV_IGNORE; + UVOptionsStdIO_[0].data.stream = nullptr; + // stdout + UVOptionsStdIO_[1].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream(); + // stderr + UVOptionsStdIO_[2].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream(); + + // -- Setup process options + std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0); + UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit; + UVOptions_.file = CommandPtr_[0]; + UVOptions_.args = const_cast<char**>(&CommandPtr_.front()); + UVOptions_.cwd = Setup_.WorkingDirectory.c_str(); + UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE; + UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size()); + UVOptions_.stdio = &UVOptionsStdIO_.front(); + + // -- Spawn process + if (UVProcess_.spawn(*uv_loop, UVOptions_, this) != 0) { + Result()->ErrorMessage = "libuv process spawn failed"; + } + } + // -- Start reading from stdio streams + if (!Result()->error()) { + if (UVPipeOut_.startRead(&Result()->StdOut) != 0) { + Result()->ErrorMessage = "libuv start reading from stdout pipe failed"; + } + } + if (!Result()->error()) { + if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut + : &Result()->StdErr) != 0) { + Result()->ErrorMessage = "libuv start reading from stderr pipe failed"; + } + } + + if (!Result()->error()) { + IsStarted_ = true; + FinishedCallback_ = std::move(finishedCallback); + } else { + // Clear libuv handles and finish + UVProcess_.reset(); + UVPipeOut_.reset(); + UVPipeErr_.reset(); + CommandPtr_.clear(); + } + + return IsStarted(); +} + +void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle, + int64_t exitStatus, + int termSignal) +{ + auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data); + if (proc.IsStarted() && !proc.IsFinished()) { + // Set error message on demand + proc.Result()->ExitStatus = exitStatus; + proc.Result()->TermSignal = termSignal; + if (!proc.Result()->error()) { + if (termSignal != 0) { + proc.Result()->ErrorMessage = "Process was terminated by signal "; + proc.Result()->ErrorMessage += + std::to_string(proc.Result()->TermSignal); + } else if (exitStatus != 0) { + proc.Result()->ErrorMessage = "Process failed with return value "; + proc.Result()->ErrorMessage += + std::to_string(proc.Result()->ExitStatus); + } + } + + // Reset process handle and try to finish + proc.UVProcess_.reset(); + proc.UVTryFinish(); + } +} + +void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish() +{ + // There still might be data in the pipes after the process has finished. + // Therefore check if the process is finished AND all pipes are closed before + // signaling the worker thread to continue. + if (UVProcess_.get() == nullptr) { + if (UVPipeOut_.uv_pipe() == nullptr) { + if (UVPipeErr_.uv_pipe() == nullptr) { + IsFinished_ = true; + FinishedCallback_(); + } + } + } +} + +cmQtAutoGenerator::cmQtAutoGenerator() + : FileSys_(&Logger_) +{ + // Initialize logger + Logger_.SetVerbose(cmSystemTools::HasEnv("VERBOSE")); + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + Logger_.SetColorOutput(cmSystemTools::IsOn(colorEnv.c_str())); + } else { + Logger_.SetColorOutput(true); + } + } + + // Initialize libuv loop + uv_disable_stdio_inheritance(); +#ifdef CMAKE_UV_SIGNAL_HACK + UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>(); +#endif + UVLoop_ = cm::make_unique<uv_loop_t>(); + uv_loop_init(UVLoop()); +} + +cmQtAutoGenerator::~cmQtAutoGenerator() +{ + // Close libuv loop + uv_loop_close(UVLoop()); +} + +bool cmQtAutoGenerator::Run(std::string const& infoFile, + std::string const& config) +{ + // Info settings + InfoFile_ = infoFile; + cmSystemTools::ConvertToUnixSlashes(InfoFile_); + InfoDir_ = cmSystemTools::GetFilenamePath(infoFile); + InfoConfig_ = config; + + bool success = false; + { + cmake cm(cmake::RoleScript); + cm.SetHomeOutputDirectory(InfoDir()); + cm.SetHomeDirectory(InfoDir()); + cm.GetCurrentSnapshot().SetDefaultDefinitions(); + cmGlobalGenerator gg(&cm); + + cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentBinary(InfoDir()); + snapshot.GetDirectory().SetCurrentSource(InfoDir()); + + auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); + // The OLD/WARN behavior for policy CMP0053 caused a speed regression. + // https://gitlab.kitware.com/cmake/cmake/issues/17570 + makefile->SetPolicyVersion("3.9"); + gg.SetCurrentMakefile(makefile.get()); + success = this->Init(makefile.get()); + } + if (success) { + success = this->Process(); + } + return success; +} + +std::string cmQtAutoGenerator::SettingsFind(std::string const& content, + const char* key) +{ + std::string prefix(key); + prefix += ':'; + std::string::size_type pos = content.find(prefix); + if (pos != std::string::npos) { + pos += prefix.size(); + if (pos < content.size()) { + std::string::size_type posE = content.find('\n', pos); + if ((posE != std::string::npos) && (posE != pos)) { + return content.substr(pos, posE - pos); + } + } + } + return std::string(); } diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 285340d..6b35234 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -6,71 +6,283 @@ #include "cmConfigure.h" // IWYU pragma: keep #include "cmQtAutoGen.h" +#include "cmUVHandlePtr.h" +#include "cm_uv.h" +#include <array> +#include <functional> +#include <mutex> +#include <stddef.h> +#include <stdint.h> #include <string> #include <vector> class cmMakefile; -class cmQtAutoGenerator +/// @brief Base class for QtAutoGen gernerators +class cmQtAutoGenerator : public cmQtAutoGen { CM_DISABLE_COPY(cmQtAutoGenerator) public: + // -- Types + + /// @brief Thread safe logging + class Logger + { + public: + // -- Verbosity + bool Verbose() const { return this->Verbose_; } + void SetVerbose(bool value); + bool ColorOutput() const { return this->ColorOutput_; } + void SetColorOutput(bool value); + // -- Log info + void Info(GeneratorT genType, std::string const& message); + // -- Log warning + void Warning(GeneratorT genType, std::string const& message); + void WarningFile(GeneratorT genType, std::string const& filename, + std::string const& message); + // -- Log error + void Error(GeneratorT genType, std::string const& message); + void ErrorFile(GeneratorT genType, std::string const& filename, + std::string const& message); + void ErrorCommand(GeneratorT genType, std::string const& message, + std::vector<std::string> const& command, + std::string const& output); + + private: + static std::string HeadLine(std::string const& title); + + private: + std::mutex Mutex_; + bool volatile Verbose_ = false; + bool volatile ColorOutput_ = false; + }; + + /// @brief Thread safe file system interface + class FileSystem + { + public: + FileSystem(Logger* log) + : Log_(log) + { + } + + Logger* Log() const { return Log_; } + std::string RealPath(std::string const& filename); + bool FileExists(std::string const& filename); + bool FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error = nullptr); + + bool FileRead(std::string& content, std::string const& filename, + std::string* error = nullptr); + /// @brief Error logging version + bool FileRead(GeneratorT genType, std::string& content, + std::string const& filename); + + bool FileWrite(std::string const& filename, std::string const& content, + std::string* error = nullptr); + /// @brief Error logging version + bool FileWrite(GeneratorT genType, std::string const& filename, + std::string const& content); + + bool FileDiffers(std::string const& filename, std::string const& content); + + bool FileRemove(std::string const& filename); + bool Touch(std::string const& filename); + + bool MakeDirectory(std::string const& dirname); + /// @brief Error logging version + bool MakeDirectory(GeneratorT genType, std::string const& dirname); + + bool MakeParentDirectory(std::string const& filename); + /// @brief Error logging version + bool MakeParentDirectory(GeneratorT genType, std::string const& filename); + + private: + std::mutex Mutex_; + Logger* Log_; + }; + + /// @brief Return value and output of an external process + struct ProcessResultT + { + void reset(); + bool error() const + { + return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty(); + } + + std::int64_t ExitStatus = 0; + int TermSignal = 0; + std::string StdOut; + std::string StdErr; + std::string ErrorMessage; + }; + + /// @brief External process management class + struct ReadOnlyProcessT + { + // -- Types + + /// @brief libuv pipe buffer class + class PipeT + { + public: + int init(uv_loop_t* uv_loop, ReadOnlyProcessT* process); + int startRead(std::string* target); + void reset(); + + // -- Libuv casts + uv_pipe_t* uv_pipe() { return UVPipe_.get(); } + uv_stream_t* uv_stream() + { + return reinterpret_cast<uv_stream_t*>(uv_pipe()); + } + uv_handle_t* uv_handle() + { + return reinterpret_cast<uv_handle_t*>(uv_pipe()); + } + + // -- Libuv callbacks + static void UVAlloc(uv_handle_t* handle, size_t suggestedSize, + uv_buf_t* buf); + static void UVData(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf); + + private: + ReadOnlyProcessT* Process_ = nullptr; + std::string* Target_ = nullptr; + std::vector<char> Buffer_; + cm::uv_pipe_ptr UVPipe_; + }; + + /// @brief Process settings + struct SetupT + { + std::string WorkingDirectory; + std::vector<std::string> Command; + ProcessResultT* Result = nullptr; + bool MergedOutput = false; + }; + + // -- Constructor + ReadOnlyProcessT() = default; + + // -- Const accessors + const SetupT& Setup() const { return Setup_; } + ProcessResultT* Result() const { return Setup_.Result; } + bool IsStarted() const { return IsStarted_; } + bool IsFinished() const { return IsFinished_; } + + // -- Runtime + void setup(ProcessResultT* result, bool mergedOutput, + std::vector<std::string> const& command, + std::string const& workingDirectory = std::string()); + bool start(uv_loop_t* uv_loop, std::function<void()>&& finishedCallback); + + private: + // -- Friends + friend class PipeT; + // -- Libuv callbacks + static void UVExit(uv_process_t* handle, int64_t exitStatus, + int termSignal); + void UVTryFinish(); + + // -- Setup + SetupT Setup_; + // -- Runtime + bool IsStarted_ = false; + bool IsFinished_ = false; + std::function<void()> FinishedCallback_; + std::vector<const char*> CommandPtr_; + std::array<uv_stdio_container_t, 3> UVOptionsStdIO_; + uv_process_options_t UVOptions_; + cm::uv_process_ptr UVProcess_; + PipeT UVPipeOut_; + PipeT UVPipeErr_; + }; + +#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \ + UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19 +#define CMAKE_UV_SIGNAL_HACK + /* + libuv does not use SA_RESTART on its signal handler, but C++ streams + depend on it for reliable i/o operations. This RAII helper convinces + libuv to install its handler, and then revises the handler to add the + SA_RESTART flag. We use a distinct uv loop that never runs to avoid + ever really getting a callback. libuv may fill the hack loop's signal + pipe and then stop writing, but that won't break any real loops. + */ + class cmUVSignalHackRAII + { + uv_loop_t HackLoop; + cm::uv_signal_ptr HackSignal; + static void HackCB(uv_signal_t*, int) {} + public: + cmUVSignalHackRAII() + { + uv_loop_init(&this->HackLoop); + this->HackSignal.init(this->HackLoop); + this->HackSignal.start(HackCB, SIGCHLD); + struct sigaction hack_sa; + sigaction(SIGCHLD, NULL, &hack_sa); + if (!(hack_sa.sa_flags & SA_RESTART)) { + hack_sa.sa_flags |= SA_RESTART; + sigaction(SIGCHLD, &hack_sa, NULL); + } + } + ~cmUVSignalHackRAII() + { + this->HackSignal.stop(); + uv_loop_close(&this->HackLoop); + } + }; +#endif + +public: + // -- Constructors cmQtAutoGenerator(); - virtual ~cmQtAutoGenerator() = default; + virtual ~cmQtAutoGenerator(); + + // -- Run bool Run(std::string const& infoFile, std::string const& config); - std::string const& GetInfoFile() const { return InfoFile; } - std::string const& GetInfoDir() const { return InfoDir; } - std::string const& GetInfoConfig() const { return InfoConfig; } - bool GetVerbose() const { return Verbose; } + // -- Accessors + // Logging + Logger& Log() { return Logger_; } + // File System + FileSystem& FileSys() { return FileSys_; } + // InfoFile + std::string const& InfoFile() const { return InfoFile_; } + std::string const& InfoDir() const { return InfoDir_; } + std::string const& InfoConfig() const { return InfoConfig_; } + // libuv loop + uv_loop_t* UVLoop() { return UVLoop_.get(); } + cm::uv_async_ptr& UVRequest() { return UVRequest_; } -protected: - // -- Central processing - virtual bool Process(cmMakefile* makefile) = 0; - - // -- Log info - void LogBold(std::string const& message) const; - void LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const; - // -- Log warning - void LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - void LogCommandError(cmQtAutoGen::Generator genType, - std::string const& message, - std::vector<std::string> const& command, - std::string const& output) const; // -- Utility - bool MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const; - bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr); - bool FileRead(std::string& content, std::string const& filename, - std::string* error = nullptr); - bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, - std::string const& content); - bool FileDiffers(std::string const& filename, std::string const& content); - bool RunCommand(std::vector<std::string> const& command, - std::string& output) const; + static std::string SettingsFind(std::string const& content, const char* key); + +protected: + // -- Abstract processing interface + virtual bool Init(cmMakefile* makefile) = 0; + virtual bool Process() = 0; private: + // -- Logging + Logger Logger_; + FileSystem FileSys_; // -- Info settings - std::string InfoFile; - std::string InfoDir; - std::string InfoConfig; - // -- Settings - bool Verbose; - bool ColorOutput; + std::string InfoFile_; + std::string InfoDir_; + std::string InfoConfig_; +// -- libuv loop +#ifdef CMAKE_UV_SIGNAL_HACK + std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_; +#endif + std::unique_ptr<uv_loop_t> UVLoop_; + cm::uv_async_ptr UVRequest_; }; #endif diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx index bce148e..4b02e0b 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -5,16 +5,15 @@ #include <algorithm> #include <array> +#include <functional> #include <list> #include <memory> #include <sstream> -#include <string.h> #include <utility> #include "cmAlgorithms.h" #include "cmCryptoHash.h" #include "cmMakefile.h" -#include "cmOutputConverter.h" #include "cmSystemTools.h" #include "cmake.h" @@ -22,51 +21,1126 @@ #include <unistd.h> #endif -// -- Static variables +// -- Class methods -static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH"; -static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; +std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath( + std::string const& relativePath) const +{ + return cmSystemTools::CollapseCombinedPath(AutogenBuildDir, relativePath); +} -// -- Static functions +/** + * @brief Tries to find the header file to the given file base path by + * appending different header extensions + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader( + std::string& header, std::string const& testBasePath) const +{ + for (std::string const& ext : HeaderExtensions) { + std::string testFilePath(testBasePath); + testFilePath.push_back('.'); + testFilePath += ext; + if (FileSys->FileExists(testFilePath)) { + header = testFilePath; + return true; + } + } + return false; +} -static std::string SubDirPrefix(std::string const& fileName) +bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped( + std::string const& fileName) const { - std::string res(cmSystemTools::GetFilenamePath(fileName)); - if (!res.empty()) { - res += '/'; + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); +} + +/** + * @brief Returns the first relevant Qt macro name found in the given C++ code + * @return The name of the Qt macro or an empty string + */ +std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro( + std::string const& content) const +{ + for (KeyExpT const& filter : MacroFilters) { + // Run a simple find string operation before the expensive + // regular expression check + if (content.find(filter.Key) != std::string::npos) { + cmsys::RegularExpressionMatch match; + if (filter.Exp.find(content.c_str(), match)) { + // Return macro name on demand + return filter.Key; + } + } + } + return std::string(); +} + +std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const +{ + std::string res; + const auto itB = MacroFilters.cbegin(); + const auto itE = MacroFilters.cend(); + const auto itL = itE - 1; + auto itC = itB; + for (; itC != itE; ++itC) { + // Separator + if (itC != itB) { + if (itC != itL) { + res += ", "; + } else { + res += " or "; + } + } + // Key + res += itC->Key; } return res; } -static bool ListContains(std::vector<std::string> const& list, - std::string const& entry) +std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile( + std::string const& sourcePath, std::string const& includeString) const +{ + // Search in vicinity of the source + { + std::string testPath = sourcePath; + testPath += includeString; + if (FileSys->FileExists(testPath)) { + return FileSys->RealPath(testPath); + } + } + // Search in include directories + for (std::string const& path : IncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeString; + if (FileSys->FileExists(fullPath)) { + return FileSys->RealPath(fullPath); + } + } + // Return empty string + return std::string(); +} + +void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies( + std::string const& content, std::set<std::string>& depends) const { - return (std::find(list.begin(), list.end(), entry) != list.end()); + if (!DependFilters.empty() && !content.empty()) { + for (KeyExpT const& filter : DependFilters) { + // Run a simple find string check + if (content.find(filter.Key) != std::string::npos) { + // Run the expensive regular expression check loop + const char* contentChars = content.c_str(); + cmsys::RegularExpressionMatch match; + while (filter.Exp.find(contentChars, match)) { + { + std::string dep = match.match(1); + if (!dep.empty()) { + depends.emplace(std::move(dep)); + } + } + contentChars += match.end(); + } + } + } + } } -// -- Class methods +bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped( + std::string const& fileName) const +{ + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); +} + +void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk) +{ + if (AutoMoc && Header) { + // Don't parse header for moc if the file is included by a source already + if (wrk.Gen().ParallelMocIncluded(FileName)) { + AutoMoc = false; + } + } + + if (AutoMoc || AutoUic) { + std::string error; + MetaT meta; + if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) { + if (!meta.Content.empty()) { + meta.FileDir = SubDirPrefix(FileName); + meta.FileBase = + cmSystemTools::GetFilenameWithoutLastExtension(FileName); + + bool success = true; + if (AutoMoc) { + if (Header) { + success = ParseMocHeader(wrk, meta); + } else { + success = ParseMocSource(wrk, meta); + } + } + if (AutoUic && success) { + ParseUic(wrk, meta); + } + } else { + wrk.LogFileWarning(GeneratorT::GEN, FileName, + "The source file is empty"); + } + } else { + wrk.LogFileError(GeneratorT::GEN, FileName, + "Could not read the file: " + error); + } + } +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, + MetaT const& meta) +{ + struct JobPre + { + bool self; // source file is self + bool underscore; // "moc_" style include + std::string SourceFile; + std::string IncludeString; + }; + + struct MocInclude + { + std::string Inc; // full include string + std::string Dir; // include string directory + std::string Base; // include string file base + }; + + // Check if this source file contains a relevant macro + std::string const ownMacro = wrk.Moc().FindMacro(meta.Content); + + // Extract moc includes from file + std::deque<MocInclude> mocIncsUsc; + std::deque<MocInclude> mocIncsDot; + { + if (meta.Content.find("moc") != std::string::npos) { + const char* contentChars = meta.Content.c_str(); + cmsys::RegularExpressionMatch match; + while (wrk.Moc().RegExpInclude.find(contentChars, match)) { + std::string incString = match.match(1); + std::string incDir(SubDirPrefix(incString)); + std::string incBase = + cmSystemTools::GetFilenameWithoutLastExtension(incString); + if (cmHasLiteralPrefix(incBase, "moc_")) { + // moc_<BASE>.cxx + // Remove the moc_ part from the base name + mocIncsUsc.emplace_back(MocInclude{ + std::move(incString), std::move(incDir), incBase.substr(4) }); + } else { + // <BASE>.moc + mocIncsDot.emplace_back(MocInclude{ + std::move(incString), std::move(incDir), std::move(incBase) }); + } + // Forward content pointer + contentChars += match.end(); + } + } + } + + // Check if there is anything to do + if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) { + return true; + } + + bool ownDotMocIncluded = false; + bool ownMocUscIncluded = false; + std::deque<JobPre> jobs; + + // Process moc_<BASE>.cxx includes + for (const MocInclude& mocInc : mocIncsUsc) { + std::string const header = + MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (wrk.Moc().skipped(header)) { + continue; + } + // Register moc job + const bool ownMoc = (mocInc.Base == meta.FileBase); + jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc }); + // Store meta information for relaxed mode + if (ownMoc) { + ownMocUscIncluded = true; + } + } else { + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but the header "; + emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += " could not be found."; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + + // Process <BASE>.moc includes + for (const MocInclude& mocInc : mocIncsDot) { + const bool ownMoc = (mocInc.Base == meta.FileBase); + if (wrk.Moc().RelaxedMode) { + // Relaxed mode + if (!ownMacro.empty() && ownMoc) { + // Add self + jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); + ownDotMocIncluded = true; + } else { + // In relaxed mode try to find a header instead but issue a warning. + // This is for KDE4 compatibility + std::string const header = + MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (wrk.Moc().skipped(header)) { + continue; + } + // Register moc job + jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc }); + if (ownMacro.empty()) { + if (ownMoc) { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += wrk.Moc().MacrosString(); + emsg += " macro.\nRunning moc on\n "; + emsg += Quoted(header); + emsg += "!\nBetter include "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for a compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + } else { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += " instead of "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += ".\nRunning moc on\n "; + emsg += Quoted(header); + emsg += "!\nBetter include "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + } + } + } else { + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a " + "matching header "; + emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += " could not be found."; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + } else { + // Strict mode + if (ownMoc) { + // Include self + jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); + ownDotMocIncluded = true; + // Accept but issue a warning if moc isn't required + if (ownMacro.empty()) { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += wrk.Moc().MacrosString(); + emsg += " macro."; + wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + } + } else { + // Don't allow <BASE>.moc include other than self in strict mode + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nThis is not supported. Include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += " to run moc on this source file."; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + } + + if (!ownMacro.empty() && !ownDotMocIncluded) { + // In this case, check whether the scanned file itself contains a + // Q_OBJECT. + // If this is the case, the moc_foo.cpp should probably be generated from + // foo.cpp instead of foo.h, because otherwise it won't build. + // But warn, since this is not how it is supposed to be used. + // This is for KDE4 compatibility. + if (wrk.Moc().RelaxedMode && ownMocUscIncluded) { + JobPre uscJobPre; + // Remove underscore job request + { + auto itC = jobs.begin(); + auto itE = jobs.end(); + for (; itC != itE; ++itC) { + JobPre& job(*itC); + if (job.self && job.underscore) { + uscJobPre = std::move(job); + jobs.erase(itC); + break; + } + } + } + // Issue a warning + { + std::string emsg = "The file contains a "; + emsg += ownMacro; + emsg += " macro, but does not include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += ". Instead it includes "; + emsg += Quoted(uscJobPre.IncludeString); + emsg += ".\nRunning moc on\n "; + emsg += Quoted(FileName); + emsg += "!\nBetter include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; + wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + } + // Add own source job + jobs.emplace_back( + JobPre{ true, false, FileName, uscJobPre.IncludeString }); + } else { + // Otherwise always error out since it will not compile. + { + std::string emsg = "The file contains a "; + emsg += ownMacro; + emsg += " macro, but does not include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += "!\nConsider to\n - add #include \""; + emsg += meta.FileBase; + emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + + // Convert pre jobs to actual jobs + for (JobPre& jobPre : jobs) { + JobHandleT jobHandle(new JobMocT(std::move(jobPre.SourceFile), FileName, + std::move(jobPre.IncludeString))); + if (jobPre.self) { + // Read depdendencies from this source + static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); + } + if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) { + return false; + } + } + return true; +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk, + MetaT const& meta) +{ + bool success = true; + std::string const macroName = wrk.Moc().FindMacro(meta.Content); + if (!macroName.empty()) { + JobHandleT jobHandle( + new JobMocT(std::string(FileName), std::string(), std::string())); + // Read depdendencies from this source + static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); + success = wrk.Gen().ParallelJobPushMoc(jobHandle); + } + return success; +} + +std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders( + WorkerT& wrk, std::string const& fileBase) const +{ + std::string res = fileBase; + res += ".{"; + res += cmJoin(wrk.Base().HeaderExtensions, ","); + res += "}"; + return res; +} + +std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader( + WorkerT& wrk, std::string const& includerDir, std::string const& includeBase) +{ + std::string header; + // Search in vicinity of the source + if (!wrk.Base().FindHeader(header, includerDir + includeBase)) { + // Search in include directories + for (std::string const& path : wrk.Moc().IncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeBase; + if (wrk.Base().FindHeader(header, fullPath)) { + break; + } + } + } + // Sanitize + if (!header.empty()) { + header = wrk.FileSys().RealPath(header); + } + return header; +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk, + MetaT const& meta) +{ + bool success = true; + if (meta.Content.find("ui_") != std::string::npos) { + const char* contentChars = meta.Content.c_str(); + cmsys::RegularExpressionMatch match; + while (wrk.Uic().RegExpInclude.find(contentChars, match)) { + if (!ParseUicInclude(wrk, meta, match.match(1))) { + success = false; + break; + } + contentChars += match.end(); + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude( + WorkerT& wrk, MetaT const& meta, std::string&& includeString) +{ + bool success = false; + std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString); + if (!uiInputFile.empty()) { + if (!wrk.Uic().skipped(uiInputFile)) { + JobHandleT jobHandle(new JobUicT(std::move(uiInputFile), FileName, + std::move(includeString))); + success = wrk.Gen().ParallelJobPushUic(jobHandle); + } else { + // A skipped file is successful + success = true; + } + } + return success; +} + +std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( + WorkerT& wrk, MetaT const& meta, std::string const& includeString) +{ + std::string res; + std::string searchFile = + cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); + searchFile += ".ui"; + // Collect search paths list + std::deque<std::string> testFiles; + { + std::string const searchPath = SubDirPrefix(includeString); + + std::string searchFileFull; + if (!searchPath.empty()) { + searchFileFull = searchPath; + searchFileFull += searchFile; + } + // Vicinity of the source + { + std::string const sourcePath = meta.FileDir; + testFiles.push_back(sourcePath + searchFile); + if (!searchPath.empty()) { + testFiles.push_back(sourcePath + searchFileFull); + } + } + // AUTOUIC search paths + if (!wrk.Uic().SearchPaths.empty()) { + for (std::string const& sPath : wrk.Uic().SearchPaths) { + testFiles.push_back((sPath + "/").append(searchFile)); + } + if (!searchPath.empty()) { + for (std::string const& sPath : wrk.Uic().SearchPaths) { + testFiles.push_back((sPath + "/").append(searchFileFull)); + } + } + } + } + + // Search for the .ui file! + for (std::string const& testFile : testFiles) { + if (wrk.FileSys().FileExists(testFile)) { + res = wrk.FileSys().RealPath(testFile); + break; + } + } + + // Log error + if (res.empty()) { + std::string emsg = "Could not find "; + emsg += Quoted(searchFile); + emsg += " in\n"; + for (std::string const& testFile : testFiles) { + emsg += " "; + emsg += Quoted(testFile); + emsg += "\n"; + } + wrk.LogFileError(GeneratorT::UIC, FileName, emsg); + } + + return res; +} + +void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk) +{ + // (Re)generate moc_predefs.h on demand + bool generate(false); + bool fileExists(wrk.FileSys().FileExists(wrk.Moc().PredefsFileAbs)); + if (!fileExists) { + if (wrk.Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(wrk.Moc().PredefsFileRel); + reason += " because it doesn't exist"; + wrk.LogInfo(GeneratorT::MOC, reason); + } + generate = true; + } else if (wrk.Moc().SettingsChanged) { + if (wrk.Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(wrk.Moc().PredefsFileRel); + reason += " because the settings changed."; + wrk.LogInfo(GeneratorT::MOC, reason); + } + generate = true; + } + if (generate) { + ProcessResultT result; + { + // Compose command + std::vector<std::string> cmd = wrk.Moc().PredefsCmd; + // Add includes + cmd.insert(cmd.end(), wrk.Moc().Includes.begin(), + wrk.Moc().Includes.end()); + // Add definitions + for (std::string const& def : wrk.Moc().Definitions) { + cmd.push_back("-D" + def); + } + // Execute command + if (!wrk.RunProcess(GeneratorT::MOC, result, cmd)) { + std::string emsg = "The content generation command for "; + emsg += Quoted(wrk.Moc().PredefsFileRel); + emsg += " failed.\n"; + emsg += result.ErrorMessage; + wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut); + } + } + + // (Re)write predefs file only on demand + if (!result.error()) { + if (!fileExists || + wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) { + if (wrk.FileSys().FileWrite(GeneratorT::MOC, wrk.Moc().PredefsFileAbs, + result.StdOut)) { + // Success + } else { + std::string emsg = "Writing "; + emsg += Quoted(wrk.Moc().PredefsFileRel); + emsg += " failed."; + wrk.LogFileError(GeneratorT::MOC, wrk.Moc().PredefsFileAbs, emsg); + } + } else { + // Touch to update the time stamp + if (wrk.Log().Verbose()) { + std::string msg = "Touching "; + msg += Quoted(wrk.Moc().PredefsFileRel); + msg += "."; + wrk.LogInfo(GeneratorT::MOC, msg); + } + wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs); + } + } + } +} + +void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies( + WorkerT& wrk, std::string const& content) +{ + wrk.Moc().FindDependencies(content, Depends); + DependsValid = true; +} + +void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk) +{ + // Compute build file name + if (!IncludeString.empty()) { + BuildFile = wrk.Base().AutogenIncludeDirAbs; + BuildFile += IncludeString; + } else { + std::string buildRel = wrk.Base().FilePathChecksum.getPart(SourceFile); + buildRel += '/'; + buildRel += "moc_"; + buildRel += cmSystemTools::GetFilenameWithoutLastExtension(SourceFile); + if (wrk.Base().MultiConfig != MultiConfigT::SINGLE) { + buildRel += wrk.Base().ConfigSuffix; + } + buildRel += ".cpp"; + wrk.Gen().ParallelMocAutoRegister(buildRel); + BuildFile = wrk.Base().AbsoluteBuildPath(buildRel); + } + + if (UpdateRequired(wrk)) { + GenerateMoc(wrk); + } +} + +bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) +{ + bool const verbose = wrk.Gen().Log().Verbose(); + + // Test if the build file exists + if (!wrk.FileSys().FileExists(BuildFile)) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from its source file "; + reason += Quoted(SourceFile); + reason += " because it doesn't exist"; + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + + // Test if any setting changed + if (wrk.Moc().SettingsChanged) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because the MOC settings changed"; + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + + // Test if the moc_predefs file is newer + if (!wrk.Moc().PredefsFileAbs.empty()) { + bool isOlder = false; + { + std::string error; + isOlder = wrk.FileSys().FileIsOlderThan( + BuildFile, wrk.Moc().PredefsFileAbs, &error); + if (!isOlder && !error.empty()) { + wrk.LogError(GeneratorT::MOC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than: "; + reason += Quoted(wrk.Moc().PredefsFileAbs); + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + } + + // Test if the source file is newer + { + bool isOlder = false; + { + std::string error; + isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + if (!isOlder && !error.empty()) { + wrk.LogError(GeneratorT::MOC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than its source file "; + reason += Quoted(SourceFile); + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + } + + // Test if a dependency file is newer + { + // Read dependencies on demand + if (!DependsValid) { + std::string content; + { + std::string error; + if (!wrk.FileSys().FileRead(content, SourceFile, &error)) { + std::string emsg = "Could not read file\n "; + emsg += Quoted(SourceFile); + emsg += "\nrequired by moc include "; + emsg += Quoted(IncludeString); + emsg += " in\n "; + emsg += Quoted(IncluderFile); + emsg += ".\n"; + emsg += error; + wrk.LogError(GeneratorT::MOC, emsg); + return false; + } + } + FindDependencies(wrk, content); + } + // Check dependency timestamps + std::string error; + std::string sourceDir = SubDirPrefix(SourceFile); + for (std::string const& depFileRel : Depends) { + std::string depFileAbs = + wrk.Moc().FindIncludedFile(sourceDir, depFileRel); + if (!depFileAbs.empty()) { + if (wrk.FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because it is older than it's dependency file "; + reason += Quoted(depFileAbs); + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + if (!error.empty()) { + wrk.LogError(GeneratorT::MOC, error); + return false; + } + } else { + std::string message = "Could not find dependency file "; + message += Quoted(depFileRel); + wrk.LogFileWarning(GeneratorT::MOC, SourceFile, message); + } + } + } + + return false; +} + +void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk) +{ + // Make sure the parent directory exists + if (wrk.FileSys().MakeParentDirectory(GeneratorT::MOC, BuildFile)) { + // Compose moc command + std::vector<std::string> cmd; + cmd.push_back(wrk.Moc().Executable); + // Add options + cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(), + wrk.Moc().AllOptions.end()); + // Add predefs include + if (!wrk.Moc().PredefsFileAbs.empty()) { + cmd.push_back("--include"); + cmd.push_back(wrk.Moc().PredefsFileAbs); + } + cmd.push_back("-o"); + cmd.push_back(BuildFile); + cmd.push_back(SourceFile); + + // Execute moc command + ProcessResultT result; + if (wrk.RunProcess(GeneratorT::MOC, result, cmd)) { + // Moc command success + if (IncludeString.empty()) { + // Notify the generator that a not included file changed + wrk.Gen().ParallelMocAutoUpdated(); + } + } else { + // Moc command failed + { + std::string emsg = "The moc process failed to compile\n "; + emsg += Quoted(SourceFile); + emsg += "\ninto\n "; + emsg += Quoted(BuildFile); + emsg += ".\n"; + emsg += result.ErrorMessage; + wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut); + } + wrk.FileSys().FileRemove(BuildFile); + } + } +} + +void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk) +{ + // Compute build file name + BuildFile = wrk.Base().AutogenIncludeDirAbs; + BuildFile += IncludeString; + + if (UpdateRequired(wrk)) { + GenerateUic(wrk); + } +} + +bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) +{ + bool const verbose = wrk.Gen().Log().Verbose(); + + // Test if the build file exists + if (!wrk.FileSys().FileExists(BuildFile)) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from its source file "; + reason += Quoted(SourceFile); + reason += " because it doesn't exist"; + wrk.LogInfo(GeneratorT::UIC, reason); + } + return true; + } + + // Test if the uic settings changed + if (wrk.Uic().SettingsChanged) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because the UIC settings changed"; + wrk.LogInfo(GeneratorT::UIC, reason); + } + return true; + } + + // Test if the source file is newer + { + bool isOlder = false; + { + std::string error; + isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + if (!isOlder && !error.empty()) { + wrk.LogError(GeneratorT::UIC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than its source file "; + reason += Quoted(SourceFile); + wrk.LogInfo(GeneratorT::UIC, reason); + } + return true; + } + } + + return false; +} + +void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk) +{ + // Make sure the parent directory exists + if (wrk.FileSys().MakeParentDirectory(GeneratorT::UIC, BuildFile)) { + // Compose uic command + std::vector<std::string> cmd; + cmd.push_back(wrk.Uic().Executable); + { + std::vector<std::string> allOpts = wrk.Uic().TargetOptions; + auto optionIt = wrk.Uic().Options.find(SourceFile); + if (optionIt != wrk.Uic().Options.end()) { + UicMergeOptions(allOpts, optionIt->second, + (wrk.Base().QtVersionMajor == 5)); + } + cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); + } + cmd.push_back("-o"); + cmd.push_back(BuildFile); + cmd.push_back(SourceFile); + + ProcessResultT result; + if (wrk.RunProcess(GeneratorT::UIC, result, cmd)) { + // Success + } else { + // Command failed + { + std::string emsg = "The uic process failed to compile\n "; + emsg += Quoted(SourceFile); + emsg += "\ninto\n "; + emsg += Quoted(BuildFile); + emsg += "\nincluded by\n "; + emsg += Quoted(IncluderFile); + emsg += ".\n"; + emsg += result.ErrorMessage; + wrk.LogCommandError(GeneratorT::UIC, emsg, cmd, result.StdOut); + } + wrk.FileSys().FileRemove(BuildFile); + } + } +} + +void cmQtAutoGeneratorMocUic::JobDeleterT::operator()(JobT* job) +{ + delete job; +} + +cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen, + uv_loop_t* uvLoop) + : Gen_(gen) +{ + // Initialize uv asynchronous callback for process starting + ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this); + // Start thread + Thread_ = std::thread(&WorkerT::Loop, this); +} + +cmQtAutoGeneratorMocUic::WorkerT::~WorkerT() +{ + // Join thread + if (Thread_.joinable()) { + Thread_.join(); + } +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogInfo( + GeneratorT genType, std::string const& message) const +{ + return Log().Info(genType, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogWarning( + GeneratorT genType, std::string const& message) const +{ + return Log().Warning(genType, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning( + GeneratorT genType, std::string const& filename, + std::string const& message) const +{ + return Log().WarningFile(genType, filename, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogError( + GeneratorT genType, std::string const& message) const +{ + Gen().ParallelRegisterJobError(); + Log().Error(genType, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogFileError( + GeneratorT genType, std::string const& filename, + std::string const& message) const +{ + Gen().ParallelRegisterJobError(); + Log().ErrorFile(genType, filename, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError( + GeneratorT genType, std::string const& message, + std::vector<std::string> const& command, std::string const& output) const +{ + Gen().ParallelRegisterJobError(); + Log().ErrorCommand(genType, message, command, output); +} + +bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess( + GeneratorT genType, ProcessResultT& result, + std::vector<std::string> const& command) +{ + if (command.empty()) { + return false; + } + + // Create process instance + { + std::lock_guard<std::mutex> lock(ProcessMutex_); + Process_ = cm::make_unique<ReadOnlyProcessT>(); + Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir); + } + + // Send asynchronous process start request to libuv loop + ProcessRequest_.send(); + + // Log command + if (this->Log().Verbose()) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(command); + msg += '\n'; + this->LogInfo(genType, msg); + } + + // Wait until the process has been finished and destroyed + { + std::unique_lock<std::mutex> ulock(ProcessMutex_); + while (Process_) { + ProcessCondition_.wait(ulock); + } + } + return !result.error(); +} + +void cmQtAutoGeneratorMocUic::WorkerT::Loop() +{ + while (true) { + Gen().WorkerSwapJob(JobHandle_); + if (JobHandle_) { + JobHandle_->Process(*this); + } else { + break; + } + } +} + +void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle) +{ + auto& wrk = *reinterpret_cast<WorkerT*>(handle->data); + { + std::lock_guard<std::mutex> lock(wrk.ProcessMutex_); + if (wrk.Process_ && !wrk.Process_->IsStarted()) { + wrk.Process_->start(handle->loop, + std::bind(&WorkerT::UVProcessFinished, &wrk)); + } + } +} + +void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished() +{ + { + std::lock_guard<std::mutex> lock(ProcessMutex_); + if (Process_ && Process_->IsFinished()) { + Process_.reset(); + } + } + // Notify idling thread + ProcessCondition_.notify_one(); +} cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() - : MultiConfig(cmQtAutoGen::WRAP) - , IncludeProjectDirsBefore(false) - , QtVersionMajor(4) - , MocSettingsChanged(false) - , MocPredefsChanged(false) - , MocRelaxedMode(false) - , UicSettingsChanged(false) + : Base_(&FileSys()) + , Moc_(&FileSys()) + , Stage_(StageT::SETTINGS_READ) + , JobsRemain_(0) + , JobError_(false) + , JobThreadsAbort_(false) + , MocAutoFileUpdated_(false) { // Precompile regular expressions - this->MocRegExpInclude.compile( + Moc_.RegExpInclude.compile( "[\n][ \t]*#[ \t]*include[ \t]+" "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); - this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); + Uic_.RegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); + + // Initialize libuv asynchronous iteration request + UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this); } -bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) +cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic() +{ +} + +bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) { // -- Meta - this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); + Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); // Utility lambdas auto InfoGet = [makefile](const char* key) { @@ -87,7 +1161,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) std::string const value = makefile->GetSafeDefinition(key); std::string::size_type pos = 0; while (pos < value.size()) { - std::string::size_type next = value.find(cmQtAutoGen::listSep, pos); + std::string::size_type next = value.find(ListSep, pos); std::string::size_type length = (next != std::string::npos) ? next - pos : value.size() - pos; // Remove enclosing braces @@ -102,7 +1176,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } } pos += length; - pos += cmQtAutoGen::listSep.size(); + pos += ListSep.size(); } } return lists; @@ -112,7 +1186,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) { std::string keyConf = key; keyConf += '_'; - keyConf += this->GetInfoConfig(); + keyConf += InfoConfig(); valueConf = makefile->GetDefinition(keyConf); } if (valueConf == nullptr) { @@ -128,122 +1202,177 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) }; // -- Read info file - if (!makefile->ReadListFile(this->GetInfoFile().c_str())) { - this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "File processing failed"); + if (!makefile->ReadListFile(InfoFile().c_str())) { + Log().ErrorFile(GeneratorT::GEN, InfoFile(), "File processing failed"); return false; } // -- Meta - this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG")); - this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); - if (this->ConfigSuffix.empty()) { - this->ConfigSuffix = "_"; - this->ConfigSuffix += this->GetInfoConfig(); + Base_.MultiConfig = MultiConfigType(InfoGet("AM_MULTI_CONFIG")); + + Base_.ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); + if (Base_.ConfigSuffix.empty()) { + Base_.ConfigSuffix = "_"; + Base_.ConfigSuffix += InfoConfig(); } - this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE"); - if (this->SettingsFile.empty()) { - this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "Settings file name missing"); + SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); + if (SettingsFile_.empty()) { + Log().ErrorFile(GeneratorT::GEN, InfoFile(), "Settings file name missing"); return false; } + { + unsigned long num = Base_.NumThreads; + if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL"), &num)) { + num = std::max<unsigned long>(num, 1); + num = std::min<unsigned long>(num, ParallelMax); + Base_.NumThreads = static_cast<unsigned int>(num); + } + } + // - Files and directories - this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); - this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); - this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - this->IncludeProjectDirsBefore = + Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); + Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); + Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); + Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); + Base_.IncludeProjectDirsBefore = InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); - this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "Autogen build directory missing"); + Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); + if (Base_.AutogenBuildDir.empty()) { + Log().ErrorFile(GeneratorT::GEN, InfoFile(), + "Autogen build directory missing"); return false; } // - Qt environment - if (!cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), - &this->QtVersionMajor)) { - this->QtVersionMajor = 4; + { + unsigned long qtv = Base_.QtVersionMajor; + if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) { + Base_.QtVersionMajor = static_cast<unsigned int>(qtv); + } } - this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); - this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); // - Moc - if (this->MocEnabled()) { - this->MocSkipList = InfoGetList("AM_MOC_SKIP"); - this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); + Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); + Moc_.Enabled = !Moc().Executable.empty(); + if (Moc().Enabled) { + { + auto lst = InfoGetList("AM_MOC_SKIP"); + Moc_.SkipList.insert(lst.begin(), lst.end()); + } + Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); #ifdef _WIN32 { - std::string const win32("WIN32"); - if (!ListContains(this->MocDefinitions, win32)) { - this->MocDefinitions.push_back(win32); + std::string win32("WIN32"); + auto itB = Moc().Definitions.cbegin(); + auto itE = Moc().Definitions.cend(); + if (std::find(itB, itE, win32) == itE) { + Moc_.Definitions.emplace_back(std::move(win32)); } } #endif - this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); - this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); - { - std::vector<std::string> const MocMacroNames = - InfoGetList("AM_MOC_MACRO_NAMES"); - for (std::string const& item : MocMacroNames) { - this->MocMacroFilters.emplace_back( - item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); - } + Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); + Moc_.Options = InfoGetList("AM_MOC_OPTIONS"); + Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); + for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { + Moc_.MacroFilters.emplace_back( + item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); } { - std::vector<std::string> const mocDependFilters = - InfoGetList("AM_MOC_DEPEND_FILTERS"); - // Insert Q_PLUGIN_METADATA dependency filter - if (this->QtVersionMajor != 4) { - this->MocDependFilterPush("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\""); + auto pushFilter = [this](std::string const& key, std::string const& exp, + std::string& error) { + if (!key.empty()) { + if (!exp.empty()) { + Moc_.DependFilters.push_back(KeyExpT()); + KeyExpT& filter(Moc_.DependFilters.back()); + if (filter.Exp.compile(exp)) { + filter.Key = key; + } else { + error = "Regular expression compiling failed"; + } + } else { + error = "Regular expression is empty"; + } + } else { + error = "Key is empty"; + } + if (!error.empty()) { + error = ("AUTOMOC_DEPEND_FILTERS: " + error); + error += "\n"; + error += " Key: "; + error += Quoted(key); + error += "\n"; + error += " Exp: "; + error += Quoted(exp); + error += "\n"; + } + }; + + std::string error; + // Insert default filter for Q_PLUGIN_METADATA + if (Base().QtVersionMajor != 4) { + pushFilter("Q_PLUGIN_METADATA", "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\"", + error); } // Insert user defined dependency filters - if ((mocDependFilters.size() % 2) == 0) { - for (std::vector<std::string>::const_iterator - dit = mocDependFilters.begin(), - ditEnd = mocDependFilters.end(); - dit != ditEnd; dit += 2) { - if (!this->MocDependFilterPush(*dit, *(dit + 1))) { - return false; + { + std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); + if ((flts.size() % 2) == 0) { + for (std::vector<std::string>::iterator itC = flts.begin(), + itE = flts.end(); + itC != itE; itC += 2) { + pushFilter(*itC, *(itC + 1), error); + if (!error.empty()) { + break; + } } + } else { + Log().ErrorFile( + GeneratorT::MOC, InfoFile(), + "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + return false; } - } else { - this->LogFileError( - cmQtAutoGen::MOC, this->GetInfoFile(), - "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + } + if (!error.empty()) { + Log().ErrorFile(GeneratorT::MOC, InfoFile(), error); return false; } } - this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); + Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); + // Install moc predefs job + if (!Moc().PredefsCmd.empty()) { + JobQueues_.MocPredefs.emplace_back(new JobMocPredefsT()); + } } // - Uic - if (this->UicEnabled()) { - this->UicSkipList = InfoGetList("AM_UIC_SKIP"); - this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); + Uic_.Enabled = !Uic().Executable.empty(); + if (Uic().Enabled) { + { + auto lst = InfoGetList("AM_UIC_SKIP"); + Uic_.SkipList.insert(lst.begin(), lst.end()); + } + Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); + Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); { auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); // Compare list sizes if (sources.size() != options.size()) { std::ostringstream ost; - ost << "files/options lists sizes mismatch (" << sources.size() << "/" + ost << "files/options lists sizes missmatch (" << sources.size() << "/" << options.size() << ")"; - this->LogFileError(cmQtAutoGen::UIC, this->GetInfoFile(), ost.str()); + Log().ErrorFile(GeneratorT::UIC, InfoFile(), ost.str()); return false; } auto fitEnd = sources.cend(); auto fit = sources.begin(); auto oit = options.begin(); while (fit != fitEnd) { - this->UicOptions[*fit] = std::move(*oit); + Uic_.Options[*fit] = std::move(*oit); ++fit; ++oit; } @@ -252,54 +1381,51 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) // Initialize source file jobs { - // Utility lambdas - auto AddJob = [this](std::map<std::string, SourceJob>& jobs, - std::string&& sourceFile) { - const bool moc = !this->MocSkip(sourceFile); - const bool uic = !this->UicSkip(sourceFile); - if (moc || uic) { - SourceJob& job = jobs[std::move(sourceFile)]; - job.Moc = moc; - job.Uic = uic; - } - }; + std::hash<std::string> stringHash; + std::set<std::size_t> uniqueHeaders; // Add header jobs for (std::string& hdr : InfoGetList("AM_HEADERS")) { - AddJob(this->HeaderJobs, std::move(hdr)); + const bool moc = !Moc().skipped(hdr); + const bool uic = !Uic().skipped(hdr); + if ((moc || uic) && uniqueHeaders.emplace(stringHash(hdr)).second) { + JobQueues_.Headers.emplace_back( + new JobParseT(std::move(hdr), moc, uic, true)); + } } // Add source jobs { std::vector<std::string> sources = InfoGetList("AM_SOURCES"); // Add header(s) for the source file - for (std::string const& src : sources) { - const bool srcMoc = !this->MocSkip(src); - const bool srcUic = !this->UicSkip(src); + for (std::string& src : sources) { + const bool srcMoc = !Moc().skipped(src); + const bool srcUic = !Uic().skipped(src); if (!srcMoc && !srcUic) { continue; } // Search for the default header file and a private header - std::array<std::string, 2> headerBases; - headerBases[0] = SubDirPrefix(src); - headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src); - headerBases[1] = headerBases[0]; - headerBases[1] += "_p"; - for (std::string const& headerBase : headerBases) { - std::string header; - if (this->FindHeader(header, headerBase)) { - const bool moc = srcMoc && !this->MocSkip(header); - const bool uic = srcUic && !this->UicSkip(header); - if (moc || uic) { - SourceJob& job = this->HeaderJobs[std::move(header)]; - job.Moc = moc; - job.Uic = uic; + { + std::array<std::string, 2> bases; + bases[0] = SubDirPrefix(src); + bases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src); + bases[1] = bases[0]; + bases[1] += "_p"; + for (std::string const& headerBase : bases) { + std::string header; + if (Base().FindHeader(header, headerBase)) { + const bool moc = srcMoc && !Moc().skipped(header); + const bool uic = srcUic && !Uic().skipped(header); + if ((moc || uic) && + uniqueHeaders.emplace(stringHash(header)).second) { + JobQueues_.Headers.emplace_back( + new JobParseT(std::move(header), moc, uic, true)); + } } } } - } - // Add Source jobs - for (std::string& src : sources) { - AddJob(this->SourceJobs, std::move(src)); + // Add source job + JobQueues_.Sources.emplace_back( + new JobParseT(std::move(src), srcMoc, srcUic)); } } } @@ -308,58 +1434,58 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) // ------------------------ // Init file path checksum generator - this->FilePathChecksum.setupParentDirs( - this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, - this->ProjectBinaryDir); + Base_.FilePathChecksum.setupParentDirs( + Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir, + Base().ProjectBinaryDir); // include directory - this->AutogenIncludeDir = "include"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->AutogenIncludeDir += this->ConfigSuffix; + Base_.AutogenIncludeDirRel = "include"; + if (Base().MultiConfig != MultiConfigT::SINGLE) { + Base_.AutogenIncludeDirRel += Base().ConfigSuffix; } - this->AutogenIncludeDir += "/"; + Base_.AutogenIncludeDirRel += "/"; + Base_.AutogenIncludeDirAbs = + Base_.AbsoluteBuildPath(Base().AutogenIncludeDirRel); // Moc variables - if (this->MocEnabled()) { + if (Moc().Enabled) { // Mocs compilation file - this->MocCompFileRel = "mocs_compilation"; - if (this->MultiConfig == cmQtAutoGen::FULL) { - this->MocCompFileRel += this->ConfigSuffix; + Moc_.CompFileRel = "mocs_compilation"; + if (Base_.MultiConfig == MultiConfigT::MULTI) { + Moc_.CompFileRel += Base().ConfigSuffix; } - this->MocCompFileRel += ".cpp"; - this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocCompFileRel); + Moc_.CompFileRel += ".cpp"; + Moc_.CompFileAbs = Base_.AbsoluteBuildPath(Moc().CompFileRel); // Moc predefs file - if (!this->MocPredefsCmd.empty()) { - this->MocPredefsFileRel = "moc_predefs"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->MocPredefsFileRel += this->ConfigSuffix; + if (!Moc_.PredefsCmd.empty()) { + Moc_.PredefsFileRel = "moc_predefs"; + if (Base_.MultiConfig != MultiConfigT::SINGLE) { + Moc_.PredefsFileRel += Base().ConfigSuffix; } - this->MocPredefsFileRel += ".h"; - this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocPredefsFileRel); + Moc_.PredefsFileRel += ".h"; + Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel); } // Sort include directories on demand - if (this->IncludeProjectDirsBefore) { + if (Base().IncludeProjectDirsBefore) { // Move strings to temporary list std::list<std::string> includes; - includes.insert(includes.end(), this->MocIncludePaths.begin(), - this->MocIncludePaths.end()); - this->MocIncludePaths.clear(); - this->MocIncludePaths.reserve(includes.size()); + includes.insert(includes.end(), Moc().IncludePaths.begin(), + Moc().IncludePaths.end()); + Moc_.IncludePaths.clear(); + Moc_.IncludePaths.reserve(includes.size()); // Append project directories only { std::array<std::string const*, 2> const movePaths = { - { &this->ProjectBinaryDir, &this->ProjectSourceDir } + { &Base().ProjectBinaryDir, &Base().ProjectSourceDir } }; for (std::string const* ppath : movePaths) { std::list<std::string>::iterator it = includes.begin(); while (it != includes.end()) { std::string const& path = *it; if (cmSystemTools::StringStartsWith(path, ppath->c_str())) { - this->MocIncludePaths.push_back(path); + Moc_.IncludePaths.push_back(path); it = includes.erase(it); } else { ++it; @@ -368,14 +1494,14 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } } // Append remaining directories - this->MocIncludePaths.insert(this->MocIncludePaths.end(), - includes.begin(), includes.end()); + Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(), + includes.end()); } // Compose moc includes list { std::set<std::string> frameworkPaths; - for (std::string const& path : this->MocIncludePaths) { - this->MocIncludes.push_back("-I" + path); + for (std::string const& path : Moc().IncludePaths) { + Moc_.Includes.push_back("-I" + path); // Extract framework path if (cmHasLiteralSuffix(path, ".framework/Headers")) { // Go up twice to get to the framework root @@ -388,1346 +1514,511 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } // Append framework includes for (std::string const& path : frameworkPaths) { - this->MocIncludes.push_back("-F"); - this->MocIncludes.push_back(path); + Moc_.Includes.push_back("-F"); + Moc_.Includes.push_back(path); } } // Setup single list with all options { // Add includes - this->MocAllOptions.insert(this->MocAllOptions.end(), - this->MocIncludes.begin(), - this->MocIncludes.end()); + Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(), + Moc().Includes.end()); // Add definitions - for (std::string const& def : this->MocDefinitions) { - this->MocAllOptions.push_back("-D" + def); + for (std::string const& def : Moc().Definitions) { + Moc_.AllOptions.push_back("-D" + def); } // Add options - this->MocAllOptions.insert(this->MocAllOptions.end(), - this->MocOptions.begin(), - this->MocOptions.end()); + Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(), + Moc().Options.end()); } } return true; } -void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile) +bool cmQtAutoGeneratorMocUic::Process() +{ + // Run libuv event loop + UVRequest().send(); + if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) { + if (JobError_) { + return false; + } + } else { + return false; + } + return true; +} + +void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle) +{ + reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage(); +} + +void cmQtAutoGeneratorMocUic::PollStage() +{ + switch (Stage_) { + case StageT::SETTINGS_READ: + SettingsFileRead(); + SetStage(StageT::CREATE_DIRECTORIES); + break; + case StageT::CREATE_DIRECTORIES: + CreateDirectories(); + SetStage(StageT::PARSE_SOURCES); + break; + case StageT::PARSE_SOURCES: + if (ThreadsStartJobs(JobQueues_.Sources)) { + SetStage(StageT::PARSE_HEADERS); + } + break; + case StageT::PARSE_HEADERS: + if (ThreadsStartJobs(JobQueues_.Headers)) { + SetStage(StageT::MOC_PREDEFS); + } + break; + case StageT::MOC_PREDEFS: + if (ThreadsStartJobs(JobQueues_.MocPredefs)) { + SetStage(StageT::MOC_PROCESS); + } + break; + case StageT::MOC_PROCESS: + if (ThreadsStartJobs(JobQueues_.Moc)) { + SetStage(StageT::MOCS_COMPILATION); + } + break; + case StageT::MOCS_COMPILATION: + if (ThreadsJobsDone()) { + MocGenerateCompilation(); + SetStage(StageT::UIC_PROCESS); + } + break; + case StageT::UIC_PROCESS: + if (ThreadsStartJobs(JobQueues_.Uic)) { + SetStage(StageT::SETTINGS_WRITE); + } + break; + case StageT::SETTINGS_WRITE: + SettingsFileWrite(); + SetStage(StageT::FINISH); + break; + case StageT::FINISH: + if (ThreadsJobsDone()) { + // Clear all libuv handles + ThreadsStop(); + UVRequest().reset(); + // Set highest END stage manually + Stage_ = StageT::END; + } + break; + case StageT::END: + break; + } +} + +void cmQtAutoGeneratorMocUic::SetStage(StageT stage) +{ + if (JobError_) { + stage = StageT::FINISH; + } + // Only allow to increase the stage + if (Stage_ < stage) { + Stage_ = stage; + UVRequest().send(); + } +} + +void cmQtAutoGeneratorMocUic::SettingsFileRead() { // Compose current settings strings { cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); std::string const sep(" ~~~ "); - if (this->MocEnabled()) { + if (Moc_.Enabled) { std::string str; - str += this->MocExecutable; + str += Moc().Executable; str += sep; - str += cmJoin(this->MocAllOptions, ";"); + str += cmJoin(Moc().AllOptions, ";"); str += sep; - str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; + str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE"; str += sep; - str += cmJoin(this->MocPredefsCmd, ";"); + str += cmJoin(Moc().PredefsCmd, ";"); str += sep; - this->SettingsStringMoc = crypt.HashString(str); + SettingsStringMoc_ = crypt.HashString(str); } - if (this->UicEnabled()) { + if (Uic().Enabled) { std::string str; - str += this->UicExecutable; + str += Uic().Executable; str += sep; - str += cmJoin(this->UicTargetOptions, ";"); - for (const auto& item : this->UicOptions) { + str += cmJoin(Uic().TargetOptions, ";"); + for (const auto& item : Uic().Options) { str += sep; str += item.first; str += sep; str += cmJoin(item.second, ";"); } str += sep; - this->SettingsStringUic = crypt.HashString(str); + SettingsStringUic_ = crypt.HashString(str); } } - // Read old settings - if (makefile->ReadListFile(this->SettingsFile.c_str())) { - { - auto SMatch = [makefile](const char* key, std::string const& value) { - return (value == makefile->GetSafeDefinition(key)); - }; - if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { - this->MocSettingsChanged = true; + // Read old settings and compare + { + std::string content; + if (FileSys().FileRead(content, SettingsFile_)) { + if (Moc().Enabled) { + if (SettingsStringMoc_ != SettingsFind(content, "moc")) { + Moc_.SettingsChanged = true; + } } - if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) { - this->UicSettingsChanged = true; + if (Uic().Enabled) { + if (SettingsStringUic_ != SettingsFind(content, "uic")) { + Uic_.SettingsChanged = true; + } + } + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (Moc().SettingsChanged || Uic().SettingsChanged) { + FileSys().FileRemove(SettingsFile_); + } + } else { + // Settings file read failed + if (Moc().Enabled) { + Moc_.SettingsChanged = true; + } + if (Uic().Enabled) { + Uic_.SettingsChanged = true; } } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (this->SettingsChanged()) { - cmSystemTools::RemoveFile(this->SettingsFile); - } - } else { - // If the file could not be read re-generate everythiung. - this->MocSettingsChanged = true; - this->UicSettingsChanged = true; } } -bool cmQtAutoGeneratorMocUic::SettingsFileWrite() +void cmQtAutoGeneratorMocUic::SettingsFileWrite() { - bool success = true; + std::lock_guard<std::mutex> jobsLock(JobsMutex_); // Only write if any setting changed - if (this->SettingsChanged()) { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + - cmQtAutoGen::Quoted(this->SettingsFile)); + if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) { + if (Log().Verbose()) { + Log().Info(GeneratorT::GEN, + "Writing settings file " + Quoted(SettingsFile_)); } // Compose settings file content - std::string settings; + std::string content; { - auto SettingAppend = [&settings](const char* key, - std::string const& value) { - settings += "set("; - settings += key; - settings += " "; - settings += cmOutputConverter::EscapeForCMake(value); - settings += ")\n"; + auto SettingAppend = [&content](const char* key, + std::string const& value) { + if (!value.empty()) { + content += key; + content += ':'; + content += value; + content += '\n'; + } }; - SettingAppend(SettingsKeyMoc, this->SettingsStringMoc); - SettingAppend(SettingsKeyUic, this->SettingsStringUic); + SettingAppend("moc", SettingsStringMoc_); + SettingAppend("uic", SettingsStringUic_); } // Write settings file - if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { - this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, - "Settings file writing failed"); + if (!FileSys().FileWrite(GeneratorT::GEN, SettingsFile_, content)) { + Log().ErrorFile(GeneratorT::GEN, SettingsFile_, + "Settings file writing failed"); // Remove old settings file to trigger a full rebuild on the next run - cmSystemTools::RemoveFile(this->SettingsFile); - success = false; + FileSys().FileRemove(SettingsFile_); + RegisterJobError(); } } - return success; } -bool cmQtAutoGeneratorMocUic::Process(cmMakefile* makefile) +void cmQtAutoGeneratorMocUic::CreateDirectories() { - // the program goes through all .cpp files to see which moc files are - // included. It is not really interesting how the moc file is named, but - // what file the moc is created from. Once a moc is included the same moc - // may not be included in the mocs_compilation.cpp file anymore. - // OTOH if there's a header containing Q_OBJECT where no corresponding - // moc file is included anywhere a moc_<filename>.cpp file is created and - // included in the mocs_compilation.cpp file. - - if (!this->InitInfoFile(makefile)) { - return false; - } - // Read latest settings - this->SettingsFileRead(makefile); - // Create AUTOGEN include directory - { - std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->AutogenIncludeDir); - if (!cmSystemTools::MakeDirectory(incDirAbs)) { - this->LogFileError(cmQtAutoGen::GEN, incDirAbs, - "Could not create directory"); - return false; - } - } - - // Parse source files - for (const auto& item : this->SourceJobs) { - if (!this->ParseSourceFile(item.first, item.second)) { - return false; - } - } - // Parse header files - for (const auto& item : this->HeaderJobs) { - if (!this->ParseHeaderFile(item.first, item.second)) { - return false; - } - } - // Read missing dependency information - if (!this->ParsePostprocess()) { - return false; + if (!FileSys().MakeDirectory(GeneratorT::GEN, Base().AutogenIncludeDirAbs)) { + RegisterJobError(); } - - // Generate files - if (!this->MocGenerateAll()) { - return false; - } - if (!this->UicGenerateAll()) { - return false; - } - - if (!this->SettingsFileWrite()) { - return false; - } - - return true; } -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename, - const SourceJob& job) +bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue) { - std::string contentText; - std::string error; - bool success = this->FileRead(contentText, absFilename, &error); - if (success) { - if (!contentText.empty()) { - if (job.Moc) { - success = this->MocParseSourceContent(absFilename, contentText); - } - if (success && job.Uic) { - success = this->UicParseContent(absFilename, contentText); + bool done = false; + std::size_t queueSize = queue.size(); + + // Change the active queue + { + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + // Check if there are still unfinished jobs from the previous queue + if (JobsRemain_ == 0) { + if (!JobThreadsAbort_) { + JobQueue_.swap(queue); + JobsRemain_ = queueSize; + } else { + // Abort requested + queue.clear(); + queueSize = 0; } - } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, - "The source file is empty"); + done = true; } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, - "Could not read the source file: " + error); } - return success; -} -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename, - const SourceJob& job) -{ - std::string contentText; - std::string error; - bool success = this->FileRead(contentText, absFilename, &error); - if (success) { - if (!contentText.empty()) { - if (job.Moc) { - this->MocParseHeaderContent(absFilename, contentText); - } - if (job.Uic) { - success = this->UicParseContent(absFilename, contentText); + if (done && (queueSize != 0)) { + // Start new threads on demand + if (Workers_.empty()) { + Workers_.resize(Base().NumThreads); + for (auto& item : Workers_) { + item = cm::make_unique<WorkerT>(this, UVLoop()); } } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, - "The header file is empty"); - } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, - "Could not read the header file: " + error); - } - return success; -} - -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::ParsePostprocess() -{ - bool success = true; - // Read missing dependencies - for (auto& item : this->MocJobsIncluded) { - if (!item->DependsValid) { - std::string content; - std::string error; - if (this->FileRead(content, item->SourceFile, &error)) { - this->MocFindDepends(item->SourceFile, content, item->Depends); - item->DependsValid = true; + // Notify threads + if (queueSize == 1) { + JobsConditionRead_.notify_one(); } else { - std::string emsg = "Could not read file\n "; - emsg += item->SourceFile; - emsg += "\nrequired by moc include \""; - emsg += item->IncludeString; - emsg += "\".\n"; - emsg += error; - this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg); - success = false; - break; + JobsConditionRead_.notify_all(); } } } - return success; -} - -/** - * @brief Tests if the file should be ignored for moc scanning - * @return True if the file should be ignored - */ -bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const -{ - if (this->MocEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->MocSkipList, absFilename)) { - return false; - } - } - return true; -} -/** - * @brief Tests if the C++ content requires moc processing - * @return True if moc is required - */ -bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText, - std::string* macroName) -{ - for (KeyRegExp& filter : this->MocMacroFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (contentText.find(filter.Key) != std::string::npos) { - if (filter.RegExp.find(contentText)) { - // Return macro name on demand - if (macroName != nullptr) { - *macroName = filter.Key; - } - return true; - } - } - } - return false; + return done; } -std::string cmQtAutoGeneratorMocUic::MocStringMacros() const +void cmQtAutoGeneratorMocUic::ThreadsStop() { - std::string res; - const auto itB = this->MocMacroFilters.cbegin(); - const auto itE = this->MocMacroFilters.cend(); - const auto itL = itE - 1; - auto itC = itB; - for (; itC != itE; ++itC) { - // Separator - if (itC != itB) { - if (itC != itL) { - res += ", "; - } else { - res += " or "; - } - } - // Key - res += itC->Key; + if (!Workers_.empty()) { + // Clear all jobs + { + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + JobThreadsAbort_ = true; + JobsRemain_ -= JobQueue_.size(); + JobQueue_.clear(); + + JobQueues_.Sources.clear(); + JobQueues_.Headers.clear(); + JobQueues_.MocPredefs.clear(); + JobQueues_.Moc.clear(); + JobQueues_.Uic.clear(); + } + // Wake threads + JobsConditionRead_.notify_all(); + // Join and clear threads + Workers_.clear(); } - return res; } -std::string cmQtAutoGeneratorMocUic::MocStringHeaders( - std::string const& fileBase) const +bool cmQtAutoGeneratorMocUic::ThreadsJobsDone() { - std::string res = fileBase; - res += ".{"; - res += cmJoin(this->HeaderExtensions, ","); - res += "}"; - return res; + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + return (JobsRemain_ == 0); } -std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader( - std::string const& sourcePath, std::string const& includeBase) const +void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle) { - std::string header; - // Search in vicinity of the source - if (!this->FindHeader(header, sourcePath + includeBase)) { - // Search in include directories - for (std::string const& path : this->MocIncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeBase; - if (this->FindHeader(header, fullPath)) { - break; - } - } - } - // Sanitize - if (!header.empty()) { - header = cmSystemTools::GetRealPath(header); + bool const jobProcessed(jobHandle); + if (jobProcessed) { + jobHandle.reset(nullptr); } - return header; -} - -bool cmQtAutoGeneratorMocUic::MocFindIncludedFile( - std::string& absFile, std::string const& sourcePath, - std::string const& includeString) const -{ - bool success = false; - // Search in vicinity of the source { - std::string testPath = sourcePath; - testPath += includeString; - if (cmSystemTools::FileExists(testPath.c_str())) { - absFile = cmSystemTools::GetRealPath(testPath); - success = true; - } - } - // Search in include directories - if (!success) { - for (std::string const& path : this->MocIncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeString; - if (cmSystemTools::FileExists(fullPath.c_str())) { - absFile = cmSystemTools::GetRealPath(fullPath); - success = true; - break; + std::unique_lock<std::mutex> jobsLock(JobsMutex_); + // Reduce the remaining job count and notify the libuv loop + // when all jobs are done + if (jobProcessed) { + --JobsRemain_; + if (JobsRemain_ == 0) { + UVRequest().send(); } } - } - return success; -} - -bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key, - std::string const& regExp) -{ - std::string error; - if (!key.empty()) { - if (!regExp.empty()) { - KeyRegExp filter; - filter.Key = key; - if (filter.RegExp.compile(regExp)) { - this->MocDependFilters.push_back(std::move(filter)); - } else { - error = "Regular expression compiling failed"; - } - } else { - error = "Regular expression is empty"; - } - } else { - error = "Key is empty"; - } - if (!error.empty()) { - std::string emsg = "AUTOMOC_DEPEND_FILTERS: "; - emsg += error; - emsg += "\n"; - emsg += " Key: "; - emsg += cmQtAutoGen::Quoted(key); - emsg += "\n"; - emsg += " RegExp: "; - emsg += cmQtAutoGen::Quoted(regExp); - emsg += "\n"; - this->LogError(cmQtAutoGen::MOC, emsg); - return false; - } - return true; -} - -void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename, - std::string const& contentText, - std::set<std::string>& depends) -{ - if (this->MocDependFilters.empty() && contentText.empty()) { - return; - } - - std::vector<std::string> matches; - for (KeyRegExp& filter : this->MocDependFilters) { - // Run a simple find string check - if (contentText.find(filter.Key) != std::string::npos) { - // Run the expensive regular expression check loop - const char* contentChars = contentText.c_str(); - while (filter.RegExp.find(contentChars)) { - std::string match = filter.RegExp.match(1); - if (!match.empty()) { - matches.emplace_back(std::move(match)); - } - contentChars += filter.RegExp.end(); - } + // Wait for new jobs + while (!JobThreadsAbort_ && JobQueue_.empty()) { + JobsConditionRead_.wait(jobsLock); } - } - - if (!matches.empty()) { - std::string const sourcePath = SubDirPrefix(absFilename); - for (std::string const& match : matches) { - // Find the dependency file - std::string incFile; - if (this->MocFindIncludedFile(incFile, sourcePath, match)) { - depends.insert(incFile); - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " + - cmQtAutoGen::Quoted(absFilename) + "\n " + - cmQtAutoGen::Quoted(incFile)); - } - } else { - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - "Could not find dependency file " + - cmQtAutoGen::Quoted(match)); - } + // Try to pick up a new job handle + if (!JobThreadsAbort_ && !JobQueue_.empty()) { + jobHandle = std::move(JobQueue_.front()); + JobQueue_.pop_front(); } } } -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::MocParseSourceContent( - std::string const& absFilename, std::string const& contentText) +void cmQtAutoGeneratorMocUic::ParallelRegisterJobError() { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); - } - - auto AddJob = [this, &absFilename](std::string const& sourceFile, - std::string const& includeString, - std::string const* content) { - auto job = cm::make_unique<MocJobIncluded>(); - job->SourceFile = sourceFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - job->DependsValid = (content != nullptr); - if (job->DependsValid) { - this->MocFindDepends(sourceFile, *content, job->Depends); - } - this->MocJobsIncluded.push_back(std::move(job)); - }; - - struct MocInc - { - std::string Inc; // full include string - std::string Dir; // include string directory - std::string Base; // include string file base - }; - - // Extract moc includes from file - std::vector<MocInc> mocIncsUsc; - std::vector<MocInc> mocIncsDot; - { - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "moc") != nullptr) { - while (this->MocRegExpInclude.find(contentChars)) { - std::string incString = this->MocRegExpInclude.match(1); - std::string incDir(SubDirPrefix(incString)); - std::string incBase = - cmSystemTools::GetFilenameWithoutLastExtension(incString); - if (cmHasLiteralPrefix(incBase, "moc_")) { - // moc_<BASE>.cxx - // Remove the moc_ part from the base name - mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir), - incBase.substr(4) }); - } else { - // <BASE>.moc - mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir), - std::move(incBase) }); - } - // Forward content pointer - contentChars += this->MocRegExpInclude.end(); - } - } - } - - std::string selfMacroName; - const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName); - - // Check if there is anything to do - if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) { - return true; - } - - // Scan file variables - std::string const scanFileDir = SubDirPrefix(absFilename); - std::string const scanFileBase = - cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - // Relaxed mode variables - bool ownDotMocIncluded = false; - std::string ownMocUscInclude; - std::string ownMocUscHeader; - - // Process moc_<BASE>.cxx includes - for (const MocInc& mocInc : mocIncsUsc) { - std::string const header = - this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (this->MocSkip(header)) { - continue; - } - // Register moc job - AddJob(header, mocInc.Inc, nullptr); - // Store meta information for relaxed mode - if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) { - ownMocUscInclude = mocInc.Inc; - ownMocUscHeader = header; - } - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but could not find the header "; - emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base)); - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - - // Process <BASE>.moc includes - for (const MocInc& mocInc : mocIncsDot) { - const bool ownMoc = (mocInc.Base == scanFileBase); - if (this->MocRelaxedMode) { - // Relaxed mode - if (selfRequiresMoc && ownMoc) { - // Add self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning. - // This is for KDE4 compatibility - std::string const header = - this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (this->MocSkip(header)) { - continue; - } - // Register moc job - AddJob(header, mocInc.Inc, nullptr); - if (!selfRequiresMoc) { - if (ownMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro.\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += " instead of "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file. CMake also could not find a matching " - "header."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } else { - // Strict mode - if (ownMoc) { - // Include self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (!selfRequiresMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro."; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } else { - // Don't allow <BASE>.moc include other than self in strict mode - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nThis is not supported. Include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " to run moc on this source file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } - - if (selfRequiresMoc && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a Q_OBJECT. - // If this is the case, the moc_foo.cpp should probably be generated from - // foo.cpp instead of foo.h, because otherwise it won't build. - // But warn, since this is not how it is supposed to be used. - if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { - // This is for KDE4 compatibility: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += ". Instead it includes "; - emsg += cmQtAutoGen::Quoted(ownMocUscInclude); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(absFilename); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - - // Remove own header job - { - auto itC = this->MocJobsIncluded.begin(); - auto itE = this->MocJobsIncluded.end(); - for (; itC != itE; ++itC) { - if ((*itC)->SourceFile == ownMocUscHeader) { - if ((*itC)->IncludeString == ownMocUscInclude) { - this->MocJobsIncluded.erase(itC); - break; - } - } - } - } - // Add own source job - AddJob(absFilename, ownMocUscInclude, &contentText); - } else { - // Otherwise always error out since it will not compile: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += "!\nConsider to\n - add #include \""; - emsg += scanFileBase; - emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - return true; + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + RegisterJobError(); } -void cmQtAutoGeneratorMocUic::MocParseHeaderContent( - std::string const& absFilename, std::string const& contentText) +// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be +// locked +void cmQtAutoGeneratorMocUic::RegisterJobError() { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); - } - - auto const fit = - std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(), - [&absFilename](std::unique_ptr<MocJobIncluded> const& job) { - return job->SourceFile == absFilename; - }); - if (fit == this->MocJobsIncluded.cend()) { - if (this->MocRequired(contentText)) { - auto job = cm::make_unique<MocJobAuto>(); - job->SourceFile = absFilename; - { - std::string& bld = job->BuildFileRel; - bld = this->FilePathChecksum.getPart(absFilename); - bld += '/'; - bld += "moc_"; - bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - bld += this->ConfigSuffix; - } - bld += ".cpp"; - } - this->MocFindDepends(absFilename, contentText, job->Depends); - this->MocJobsAuto.push_back(std::move(job)); + JobError_ = true; + if (!JobThreadsAbort_) { + JobThreadsAbort_ = true; + // Clear remaining jobs + if (JobsRemain_ != 0) { + JobsRemain_ -= JobQueue_.size(); + JobQueue_.clear(); } } } -bool cmQtAutoGeneratorMocUic::MocGenerateAll() +bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle) { - if (!this->MocEnabled()) { - return true; - } - - // Look for name collisions in included moc files - { - bool collision = false; - std::map<std::string, std::vector<MocJobIncluded const*>> collisions; - for (auto const& job : this->MocJobsIncluded) { - auto& list = collisions[job->IncludeString]; - if (!list.empty()) { - collision = true; - } - list.push_back(job.get()); - } - if (collision) { - std::string emsg = - "Included moc files with the same name will be " - "generated from different sources.\n" - "Consider to\n" - " - not include the \"moc_<NAME>.cpp\" file\n" - " - add a directory prefix to a \"<NAME>.moc\" include " - "(e.g \"sub/<NAME>.moc\")\n" - " - rename the source file(s)\n" - "Include conflicts\n" - "-----------------\n"; - const auto& colls = collisions; - for (auto const& coll : colls) { - if (coll.second.size() > 1) { - emsg += cmQtAutoGen::Quoted(coll.first); - emsg += " included in\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->Includer); - emsg += "\n"; - } - emsg += "would be generated from\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->SourceFile); - emsg += "\n"; + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + if (!JobThreadsAbort_) { + bool pushJobHandle = true; + // Do additional tests if this is an included moc job + const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle)); + if (!mocJob.IncludeString.empty()) { + // Register included moc file and look for collisions + MocIncludedFiles_.emplace(mocJob.SourceFile); + if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) { + // Another source file includes the same moc file! + for (const JobHandleT& otherHandle : JobQueues_.Moc) { + const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle)); + if (otherJob.IncludeString == mocJob.IncludeString) { + // Check if the same moc file would be generated from different + // source files which is an error. + if (otherJob.SourceFile != mocJob.SourceFile) { + // Include string collision + std::string error = "The two source files\n "; + error += Quoted(mocJob.IncluderFile); + error += " and\n "; + error += Quoted(otherJob.IncluderFile); + error += "\ncontain the the same moc include string "; + error += Quoted(mocJob.IncludeString); + error += "\nbut the moc file would be generated from different " + "source files\n "; + error += Quoted(mocJob.SourceFile); + error += " and\n "; + error += Quoted(otherJob.SourceFile); + error += ".\nConsider to\n" + "- not include the \"moc_<NAME>.cpp\" file\n" + "- add a directory prefix to a \"<NAME>.moc\" include " + "(e.g \"sub/<NAME>.moc\")\n" + "- rename the source file(s)\n"; + Log().Error(GeneratorT::MOC, error); + RegisterJobError(); + } + // Do not push this job in since the included moc file already + // gets generated by an other job. + pushJobHandle = false; + break; } } } - this->LogError(cmQtAutoGen::MOC, emsg); - return false; } - } - - // (Re)generate moc_predefs.h on demand - if (!this->MocPredefsCmd.empty()) { - if (this->MocSettingsChanged || - !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { - if (this->GetVerbose()) { - this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); - } - - std::string output; - { - // Compose command - std::vector<std::string> cmd = this->MocPredefsCmd; - // Add includes - cmd.insert(cmd.end(), this->MocIncludes.begin(), - this->MocIncludes.end()); - // Add definitions - for (std::string const& def : this->MocDefinitions) { - cmd.push_back("-D" + def); - } - // Execute command - if (!this->RunCommand(cmd, output)) { - this->LogCommandError(cmQtAutoGen::MOC, - "moc_predefs generation failed", cmd, output); - return false; - } - } - - // (Re)write predefs file only on demand - if (this->FileDiffers(this->MocPredefsFileAbs, output)) { - if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - output)) { - this->MocPredefsChanged = true; - } else { - this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - "moc_predefs file writing failed"); - return false; - } - } else { - // Touch to update the time stamp - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching moc_predefs " + this->MocPredefsFileRel); - } - cmSystemTools::Touch(this->MocPredefsFileAbs, false); - } - } - - // Add moc_predefs.h to moc file dependencies - for (auto const& item : this->MocJobsIncluded) { - item->Depends.insert(this->MocPredefsFileAbs); - } - for (auto const& item : this->MocJobsAuto) { - item->Depends.insert(this->MocPredefsFileAbs); - } - } - - // Generate moc files that are included by source files. - for (auto const& item : this->MocJobsIncluded) { - if (!this->MocGenerateFile(*item)) { - return false; - } - } - // Generate moc files that are _not_ included by source files. - bool autoNameGenerated = false; - for (auto const& item : this->MocJobsAuto) { - if (!this->MocGenerateFile(*item, &autoNameGenerated)) { - return false; - } - } - - // Compose mocs compilation file content - { - std::string mocs = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (this->MocJobsAuto.empty()) { - // Placeholder content - mocs += - "// No files found that require moc or the moc files are included\n"; - mocs += "enum some_compilers { need_more_than_nothing };\n"; - } else { - // Valid content - for (const auto& item : this->MocJobsAuto) { - mocs += "#include \""; - mocs += item->BuildFileRel; - mocs += "\"\n"; - } - } - - if (this->FileDiffers(this->MocCompFileAbs, mocs)) { - // Actually write mocs compilation file - if (this->GetVerbose()) { - this->LogBold("Generating MOC compilation " + this->MocCompFileRel); - } - if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) { - this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs, - "mocs compilation file writing failed"); - return false; - } - } else if (autoNameGenerated) { - // Only touch mocs compilation file - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching mocs compilation " + this->MocCompFileRel); - } - cmSystemTools::Touch(this->MocCompFileAbs, false); + // Push job on demand + if (pushJobHandle) { + JobQueues_.Moc.emplace_back(std::move(jobHandle)); } } - - return true; + return !JobError_; } -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, - bool* generated) +bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle) { - bool success = true; - - std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, mocJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->MocSettingsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because the MOC settings changed"; - } - generate = true; - } - if (!generate && this->MocPredefsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because moc_predefs.h changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; - } - } - } - if (success && !generate) { - // Test if a dependency file is newer - std::string error; - for (std::string const& depFile : mocJob.Depends) { - if (FileIsOlderThan(mocFileAbs, depFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(depFile); + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + if (!JobThreadsAbort_) { + bool pushJobHandle = true; + // Look for include collisions. + const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle)); + for (const JobHandleT& otherHandle : JobQueues_.Uic) { + const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle)); + if (otherJob.IncludeString == uicJob.IncludeString) { + // Check if the same uic file would be generated from different + // source files which would be an error. + if (otherJob.SourceFile != uicJob.SourceFile) { + // Include string collision + std::string error = "The two source files\n "; + error += Quoted(uicJob.IncluderFile); + error += " and\n "; + error += Quoted(otherJob.IncluderFile); + error += "\ncontain the the same uic include string "; + error += Quoted(uicJob.IncludeString); + error += "\nbut the uic file would be generated from different " + "source files\n "; + error += Quoted(uicJob.SourceFile); + error += " and\n "; + error += Quoted(otherJob.SourceFile); + error += + ".\nConsider to\n" + "- add a directory prefix to a \"ui_<NAME>.h\" include " + "(e.g \"sub/ui_<NAME>.h\")\n" + "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" " + "include(s)\n"; + Log().Error(GeneratorT::UIC, error); + RegisterJobError(); } - generate = true; - break; - } - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; + // Do not push this job in since the uic file already + // gets generated by an other job. + pushJobHandle = false; break; } } - } - - if (generate) { - // Log - if (this->GetVerbose()) { - this->LogBold("Generating MOC source " + mocJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::MOC, generateReason); - } - - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) { - // Compose moc command - std::vector<std::string> cmd; - cmd.push_back(this->MocExecutable); - // Add options - cmd.insert(cmd.end(), this->MocAllOptions.begin(), - this->MocAllOptions.end()); - // Add predefs include - if (!this->MocPredefsFileAbs.empty()) { - cmd.push_back("--include"); - cmd.push_back(this->MocPredefsFileAbs); - } - cmd.push_back("-o"); - cmd.push_back(mocFileAbs); - cmd.push_back(mocJob.SourceFile); - - // Execute moc command - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - if (generated != nullptr) { - *generated = true; - } - } else { - // Moc command failed - { - std::string emsg = "moc failed for\n "; - emsg += cmQtAutoGen::Quoted(mocJob.SourceFile); - this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output); - } - cmSystemTools::RemoveFile(mocFileAbs); - success = false; - } - } else { - // Parent directory creation failed - success = false; + if (pushJobHandle) { + JobQueues_.Uic.emplace_back(std::move(jobHandle)); } } - return success; + return !JobError_; } -/** - * @brief Tests if the file name is in the skip list - */ -bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const +bool cmQtAutoGeneratorMocUic::ParallelMocIncluded( + std::string const& sourceFile) { - if (this->UicEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->UicSkipList, absFilename)) { - return false; - } - } - return true; + std::lock_guard<std::mutex> mocLock(JobsMutex_); + return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); } -bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename, - std::string const& contentText) +void cmQtAutoGeneratorMocUic::ParallelMocAutoRegister( + std::string const& mocFile) { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename); - } - - std::vector<std::string> includes; - // Extracte includes - { - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "ui_") != nullptr) { - while (this->UicRegExpInclude.find(contentChars)) { - includes.push_back(this->UicRegExpInclude.match(1)); - contentChars += this->UicRegExpInclude.end(); - } - } - } - - for (std::string const& includeString : includes) { - std::string uiInputFile; - if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) { - return false; - } - // Check if this file should be skipped - if (this->UicSkip(uiInputFile)) { - continue; - } - // Check if the job already exists - bool jobExists = false; - for (const auto& job : this->UicJobs) { - if ((job->SourceFile == uiInputFile) && - (job->IncludeString == includeString)) { - jobExists = true; - break; - } - } - if (!jobExists) { - auto job = cm::make_unique<UicJob>(); - job->SourceFile = uiInputFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - this->UicJobs.push_back(std::move(job)); - } - } - - return true; + std::lock_guard<std::mutex> mocLock(JobsMutex_); + MocAutoFiles_.emplace(mocFile); } -bool cmQtAutoGeneratorMocUic::UicFindIncludedFile( - std::string& absFile, std::string const& sourceFile, - std::string const& includeString) +void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated() { - bool success = false; - std::string searchFile = - cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); - searchFile += ".ui"; - // Collect search paths list - std::vector<std::string> testFiles; - { - std::string const searchPath = SubDirPrefix(includeString); - - std::string searchFileFull; - if (!searchPath.empty()) { - searchFileFull = searchPath; - searchFileFull += searchFile; - } - // Vicinity of the source - { - std::string const sourcePath = SubDirPrefix(sourceFile); - testFiles.push_back(sourcePath + searchFile); - if (!searchPath.empty()) { - testFiles.push_back(sourcePath + searchFileFull); - } - } - // AUTOUIC search paths - if (!this->UicSearchPaths.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFile)); - } - if (!searchPath.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFileFull)); - } - } - } - } - - // Search for the .ui file! - for (std::string const& testFile : testFiles) { - if (cmSystemTools::FileExists(testFile.c_str())) { - absFile = cmSystemTools::GetRealPath(testFile); - success = true; - break; - } - } - - // Log error - if (!success) { - std::string emsg = "Could not find "; - emsg += cmQtAutoGen::Quoted(searchFile); - emsg += " in\n"; - for (std::string const& testFile : testFiles) { - emsg += " "; - emsg += cmQtAutoGen::Quoted(testFile); - emsg += "\n"; - } - this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); - } - - return success; + std::lock_guard<std::mutex> mocLock(JobsMutex_); + MocAutoFileUpdated_ = true; } -bool cmQtAutoGeneratorMocUic::UicGenerateAll() +void cmQtAutoGeneratorMocUic::MocGenerateCompilation() { - if (!this->UicEnabled()) { - return true; - } - - // Look for name collisions in included uic files - { - bool collision = false; - std::map<std::string, std::vector<UicJob const*>> collisions; - for (auto const& job : this->UicJobs) { - auto& list = collisions[job->IncludeString]; - if (!list.empty()) { - collision = true; - } - list.push_back(job.get()); - } - if (collision) { - std::string emsg = - "Included uic files with the same name will be " - "generated from different sources.\n" - "Consider to\n" - " - add a directory prefix to a \"ui_<NAME>.h\" include " - "(e.g \"sub/ui_<NAME>.h\")\n" - " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" " - "include(s)\n" - "Include conflicts\n" - "-----------------\n"; - const auto& colls = collisions; - for (auto const& coll : colls) { - if (coll.second.size() > 1) { - emsg += cmQtAutoGen::Quoted(coll.first); - emsg += " included in\n"; - for (const UicJob* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->Includer); - emsg += "\n"; - } - emsg += "would be generated from\n"; - for (const UicJob* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->SourceFile); - emsg += "\n"; - } + std::lock_guard<std::mutex> mocLock(JobsMutex_); + if (!JobThreadsAbort_ && Moc().Enabled) { + // Compose mocs compilation file content + { + std::string content = + "// This file is autogenerated. Changes will be overwritten.\n"; + if (MocAutoFiles_.empty()) { + // Placeholder content + content += "// No files found that require moc or the moc files are " + "included\n"; + content += "enum some_compilers { need_more_than_nothing };\n"; + } else { + // Valid content + for (std::string const& mocfile : MocAutoFiles_) { + content += "#include \""; + content += mocfile; + content += "\"\n"; } } - this->LogError(cmQtAutoGen::UIC, emsg); - return false; - } - } - - // Generate ui header files - for (const auto& item : this->UicJobs) { - if (!this->UicGenerateFile(*item)) { - return false; - } - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) -{ - bool success = true; - - std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, uicJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->UicSettingsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because the UIC settings changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::UIC, error); - success = false; - } - } - } - if (generate) { - // Log - if (this->GetVerbose()) { - this->LogBold("Generating UIC header " + uicJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::UIC, generateReason); - } - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) { - // Compose uic command - std::vector<std::string> cmd; - cmd.push_back(this->UicExecutable); - { - std::vector<std::string> allOpts = this->UicTargetOptions; - auto optionIt = this->UicOptions.find(uicJob.SourceFile); - if (optionIt != this->UicOptions.end()) { - cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, - (this->QtVersionMajor == 5)); + std::string const& compRel = Moc().CompFileRel; + std::string const& compAbs = Moc().CompFileAbs; + if (FileSys().FileDiffers(compAbs, content)) { + // Actually write mocs compilation file + if (Log().Verbose()) { + Log().Info(GeneratorT::MOC, "Generating MOC compilation " + compRel); } - cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); - } - cmd.push_back("-o"); - cmd.push_back(uicFileAbs); - cmd.push_back(uicJob.SourceFile); - - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - } else { - // Command failed - { - std::string emsg = "uic failed for\n "; - emsg += cmQtAutoGen::Quoted(uicJob.SourceFile); - emsg += "\nincluded by\n "; - emsg += cmQtAutoGen::Quoted(uicJob.Includer); - this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output); + if (!FileSys().FileWrite(GeneratorT::MOC, compAbs, content)) { + Log().ErrorFile(GeneratorT::MOC, compAbs, + "mocs compilation file writing failed"); + RegisterJobError(); + return; } - cmSystemTools::RemoveFile(uicFileAbs); - success = false; + } else if (MocAutoFileUpdated_) { + // Only touch mocs compilation file + if (Log().Verbose()) { + Log().Info(GeneratorT::MOC, "Touching mocs compilation " + compRel); + } + FileSys().Touch(compAbs); } - } else { - // Parent directory creation failed - success = false; } } - return success; -} - -/** - * @brief Tries to find the header file to the given file base path by - * appending different header extensions - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header, - std::string const& testBasePath) const -{ - for (std::string const& ext : this->HeaderExtensions) { - std::string testFilePath(testBasePath); - testFilePath.push_back('.'); - testFilePath += ext; - if (cmSystemTools::FileExists(testFilePath.c_str())) { - header = testFilePath; - return true; - } - } - return false; } diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h index d510939..215e25a 100644 --- a/Source/cmQtAutoGeneratorMocUic.h +++ b/Source/cmQtAutoGeneratorMocUic.h @@ -8,188 +8,434 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" +#include "cmUVHandlePtr.h" +#include "cm_uv.h" #include "cmsys/RegularExpression.hxx" +#include <algorithm> +#include <condition_variable> +#include <cstddef> +#include <deque> #include <map> #include <memory> // IWYU pragma: keep +#include <mutex> #include <set> #include <string> +#include <thread> #include <vector> class cmMakefile; +// @brief AUTOMOC and AUTOUIC generator class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator { CM_DISABLE_COPY(cmQtAutoGeneratorMocUic) public: cmQtAutoGeneratorMocUic(); + ~cmQtAutoGeneratorMocUic() override; -private: +public: // -- Types + class WorkerT; /// @brief Search key plus regular expression pair - struct KeyRegExp + /// + struct KeyExpT { - KeyRegExp() = default; + KeyExpT() = default; - KeyRegExp(const char* key, const char* regExp) + KeyExpT(const char* key, const char* exp) : Key(key) - , RegExp(regExp) + , Exp(exp) { } - KeyRegExp(std::string const& key, std::string const& regExp) + KeyExpT(std::string const& key, std::string const& exp) : Key(key) - , RegExp(regExp) + , Exp(exp) { } std::string Key; - cmsys::RegularExpression RegExp; + cmsys::RegularExpression Exp; }; - /// @brief Source file job - struct SourceJob + /// @brief Common settings + /// + class BaseSettingsT { - bool Moc = false; - bool Uic = false; + CM_DISABLE_COPY(BaseSettingsT) + public: + // -- Volatile methods + BaseSettingsT(FileSystem* fileSystem) + : MultiConfig(MultiConfigT::WRAPPER) + , IncludeProjectDirsBefore(false) + , QtVersionMajor(4) + , NumThreads(1) + , FileSys(fileSystem) + { + } + + // -- Const methods + std::string AbsoluteBuildPath(std::string const& relativePath) const; + bool FindHeader(std::string& header, + std::string const& testBasePath) const; + + // -- Attributes + // - Config + std::string ConfigSuffix; + MultiConfigT MultiConfig; + bool IncludeProjectDirsBefore; + unsigned int QtVersionMajor; + unsigned int NumThreads; + // - Directories + std::string ProjectSourceDir; + std::string ProjectBinaryDir; + std::string CurrentSourceDir; + std::string CurrentBinaryDir; + std::string AutogenBuildDir; + std::string AutogenIncludeDirRel; + std::string AutogenIncludeDirAbs; + // - Files + cmFilePathChecksum FilePathChecksum; + std::vector<std::string> HeaderExtensions; + // - File system + FileSystem* FileSys; }; - /// @brief MOC job - struct MocJobAuto + /// @brief Moc settings + /// + class MocSettingsT { - std::string SourceFile; - std::string BuildFileRel; - std::set<std::string> Depends; + CM_DISABLE_COPY(MocSettingsT) + public: + MocSettingsT(FileSystem* fileSys) + : FileSys(fileSys) + { + } + + // -- Const methods + bool skipped(std::string const& fileName) const; + std::string FindMacro(std::string const& content) const; + std::string MacrosString() const; + std::string FindIncludedFile(std::string const& sourcePath, + std::string const& includeString) const; + void FindDependencies(std::string const& content, + std::set<std::string>& depends) const; + + // -- Attributes + bool Enabled = false; + bool SettingsChanged = false; + bool RelaxedMode = false; + std::string Executable; + std::string CompFileRel; + std::string CompFileAbs; + std::string PredefsFileRel; + std::string PredefsFileAbs; + std::set<std::string> SkipList; + std::vector<std::string> IncludePaths; + std::vector<std::string> Includes; + std::vector<std::string> Definitions; + std::vector<std::string> Options; + std::vector<std::string> AllOptions; + std::vector<std::string> PredefsCmd; + std::vector<KeyExpT> DependFilters; + std::vector<KeyExpT> MacroFilters; + cmsys::RegularExpression RegExpInclude; + // - File system + FileSystem* FileSys; }; - /// @brief MOC job - struct MocJobIncluded : MocJobAuto + /// @brief Uic settings + /// + class UicSettingsT { - bool DependsValid = false; - std::string Includer; + CM_DISABLE_COPY(UicSettingsT) + public: + UicSettingsT() = default; + // -- Const methods + bool skipped(std::string const& fileName) const; + + // -- Attributes + bool Enabled = false; + bool SettingsChanged = false; + std::string Executable; + std::set<std::string> SkipList; + std::vector<std::string> TargetOptions; + std::map<std::string, std::vector<std::string>> Options; + std::vector<std::string> SearchPaths; + cmsys::RegularExpression RegExpInclude; + }; + + /// @brief Abstract job class for threaded processing + /// + class JobT + { + CM_DISABLE_COPY(JobT) + public: + JobT() = default; + virtual ~JobT() = default; + // -- Abstract processing interface + virtual void Process(WorkerT& wrk) = 0; + }; + + /// @brief Deleter for classes derived from Job + /// + struct JobDeleterT + { + void operator()(JobT* job); + }; + + // Job management types + typedef std::unique_ptr<JobT, JobDeleterT> JobHandleT; + typedef std::deque<JobHandleT> JobQueueT; + + /// @brief Parse source job + /// + class JobParseT : public JobT + { + public: + JobParseT(std::string&& fileName, bool moc, bool uic, bool header = false) + : FileName(std::move(fileName)) + , AutoMoc(moc) + , AutoUic(uic) + , Header(header) + { + } + + private: + struct MetaT + { + std::string Content; + std::string FileDir; + std::string FileBase; + }; + + void Process(WorkerT& wrk) override; + bool ParseMocSource(WorkerT& wrk, MetaT const& meta); + bool ParseMocHeader(WorkerT& wrk, MetaT const& meta); + std::string MocStringHeaders(WorkerT& wrk, + std::string const& fileBase) const; + std::string MocFindIncludedHeader(WorkerT& wrk, + std::string const& includerDir, + std::string const& includeBase); + bool ParseUic(WorkerT& wrk, MetaT const& meta); + bool ParseUicInclude(WorkerT& wrk, MetaT const& meta, + std::string&& includeString); + std::string UicFindIncludedFile(WorkerT& wrk, MetaT const& meta, + std::string const& includeString); + + private: + std::string FileName; + bool AutoMoc = false; + bool AutoUic = false; + bool Header = false; + }; + + /// @brief Generate moc_predefs + /// + class JobMocPredefsT : public JobT + { + private: + void Process(WorkerT& wrk) override; + }; + + /// @brief Moc a file job + /// + class JobMocT : public JobT + { + public: + JobMocT(std::string&& sourceFile, std::string const& includerFile, + std::string&& includeString) + : SourceFile(std::move(sourceFile)) + , IncluderFile(includerFile) + , IncludeString(std::move(includeString)) + { + } + + void FindDependencies(WorkerT& wrk, std::string const& content); + + private: + void Process(WorkerT& wrk) override; + bool UpdateRequired(WorkerT& wrk); + void GenerateMoc(WorkerT& wrk); + + public: + std::string SourceFile; + std::string IncluderFile; std::string IncludeString; + std::string BuildFile; + bool DependsValid = false; + std::set<std::string> Depends; }; - /// @brief UIC job - struct UicJob + /// @brief Uic a file job + /// + class JobUicT : public JobT { + public: + JobUicT(std::string&& sourceFile, std::string const& includerFile, + std::string&& includeString) + : SourceFile(std::move(sourceFile)) + , IncluderFile(includerFile) + , IncludeString(std::move(includeString)) + { + } + + private: + void Process(WorkerT& wrk) override; + bool UpdateRequired(WorkerT& wrk); + void GenerateUic(WorkerT& wrk); + + public: std::string SourceFile; - std::string BuildFileRel; - std::string Includer; + std::string IncluderFile; std::string IncludeString; + std::string BuildFile; }; - // -- Initialization - bool InitInfoFile(cmMakefile* makefile); + /// @brief Worker Thread + /// + class WorkerT + { + CM_DISABLE_COPY(WorkerT) + public: + WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop); + ~WorkerT(); + + // -- Const accessors + cmQtAutoGeneratorMocUic& Gen() const { return *Gen_; } + Logger& Log() const { return Gen_->Log(); } + FileSystem& FileSys() const { return Gen_->FileSys(); } + const BaseSettingsT& Base() const { return Gen_->Base(); } + const MocSettingsT& Moc() const { return Gen_->Moc(); } + const UicSettingsT& Uic() const { return Gen_->Uic(); } - // -- Settings file - void SettingsFileRead(cmMakefile* makefile); - bool SettingsFileWrite(); - bool SettingsChanged() const + // -- Log info + void LogInfo(GeneratorT genType, std::string const& message) const; + // -- Log warning + void LogWarning(GeneratorT genType, std::string const& message) const; + void LogFileWarning(GeneratorT genType, std::string const& filename, + std::string const& message) const; + // -- Log error + void LogError(GeneratorT genType, std::string const& message) const; + void LogFileError(GeneratorT genType, std::string const& filename, + std::string const& message) const; + void LogCommandError(GeneratorT genType, std::string const& message, + std::vector<std::string> const& command, + std::string const& output) const; + + // -- External processes + /// @brief Verbose logging version + bool RunProcess(GeneratorT genType, ProcessResultT& result, + std::vector<std::string> const& command); + + private: + /// @brief Thread main loop + void Loop(); + + // -- Libuv callbacks + static void UVProcessStart(uv_async_t* handle); + void UVProcessFinished(); + + private: + // -- Generator + cmQtAutoGeneratorMocUic* Gen_; + // -- Job handle + JobHandleT JobHandle_; + // -- Process management + std::mutex ProcessMutex_; + cm::uv_async_ptr ProcessRequest_; + std::condition_variable ProcessCondition_; + std::unique_ptr<ReadOnlyProcessT> Process_; + // -- System thread + std::thread Thread_; + }; + + /// @brief Processing stage + enum class StageT { - return (this->MocSettingsChanged || this->UicSettingsChanged); - } - - // -- Central processing - bool Process(cmMakefile* makefile) override; - - // -- Source parsing - bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); - bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); - bool ParsePostprocess(); - - // -- Moc - bool MocEnabled() const { return !this->MocExecutable.empty(); } - bool MocSkip(std::string const& absFilename) const; - bool MocRequired(std::string const& contentText, - std::string* macroName = nullptr); - // Moc strings - std::string MocStringMacros() const; - std::string MocStringHeaders(std::string const& fileBase) const; - std::string MocFindIncludedHeader(std::string const& sourcePath, - std::string const& includeBase) const; - bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString) const; - // Moc depends - bool MocDependFilterPush(std::string const& key, std::string const& regExp); - void MocFindDepends(std::string const& absFilename, - std::string const& contentText, - std::set<std::string>& depends); - // Moc - bool MocParseSourceContent(std::string const& absFilename, - std::string const& contentText); - void MocParseHeaderContent(std::string const& absFilename, - std::string const& contentText); - - bool MocGenerateAll(); - bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr); - - // -- Uic - bool UicEnabled() const { return !this->UicExecutable.empty(); } - bool UicSkip(std::string const& absFilename) const; - bool UicParseContent(std::string const& fileName, - std::string const& contentText); - bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString); - bool UicGenerateAll(); - bool UicGenerateFile(const UicJob& uicJob); - - // -- Utility - bool FindHeader(std::string& header, std::string const& testBasePath) const; - - // -- Meta - std::string ConfigSuffix; - cmQtAutoGen::MultiConfig MultiConfig; + SETTINGS_READ, + CREATE_DIRECTORIES, + PARSE_SOURCES, + PARSE_HEADERS, + MOC_PREDEFS, + MOC_PROCESS, + MOCS_COMPILATION, + UIC_PROCESS, + SETTINGS_WRITE, + FINISH, + END + }; + + // -- Const settings interface + const BaseSettingsT& Base() const { return this->Base_; } + const MocSettingsT& Moc() const { return this->Moc_; } + const UicSettingsT& Uic() const { return this->Uic_; } + + // -- Worker thread interface + void WorkerSwapJob(JobHandleT& jobHandle); + // -- Parallel job processing interface + void ParallelRegisterJobError(); + bool ParallelJobPushMoc(JobHandleT& jobHandle); + bool ParallelJobPushUic(JobHandleT& jobHandle); + bool ParallelMocIncluded(std::string const& sourceFile); + void ParallelMocAutoRegister(std::string const& mocFile); + void ParallelMocAutoUpdated(); + +private: + // -- Abstract processing interface + bool Init(cmMakefile* makefile) override; + bool Process() override; + // -- Process stage + static void UVPollStage(uv_async_t* handle); + void PollStage(); + void SetStage(StageT stage); + // -- Settings file + void SettingsFileRead(); + void SettingsFileWrite(); + // -- Thread processing + bool ThreadsStartJobs(JobQueueT& queue); + bool ThreadsJobsDone(); + void ThreadsStop(); + void RegisterJobError(); + // -- Generation + void CreateDirectories(); + void MocGenerateCompilation(); + +private: // -- Settings - bool IncludeProjectDirsBefore; - std::string SettingsFile; - std::string SettingsStringMoc; - std::string SettingsStringUic; - // -- Directories - std::string ProjectSourceDir; - std::string ProjectBinaryDir; - std::string CurrentSourceDir; - std::string CurrentBinaryDir; - std::string AutogenBuildDir; - std::string AutogenIncludeDir; - // -- Qt environment - unsigned long QtVersionMajor; - std::string MocExecutable; - std::string UicExecutable; - // -- File lists - std::map<std::string, SourceJob> HeaderJobs; - std::map<std::string, SourceJob> SourceJobs; - std::vector<std::string> HeaderExtensions; - cmFilePathChecksum FilePathChecksum; - // -- Moc - bool MocSettingsChanged; - bool MocPredefsChanged; - bool MocRelaxedMode; - std::string MocCompFileRel; - std::string MocCompFileAbs; - std::string MocPredefsFileRel; - std::string MocPredefsFileAbs; - std::vector<std::string> MocSkipList; - std::vector<std::string> MocIncludePaths; - std::vector<std::string> MocIncludes; - std::vector<std::string> MocDefinitions; - std::vector<std::string> MocOptions; - std::vector<std::string> MocAllOptions; - std::vector<std::string> MocPredefsCmd; - std::vector<KeyRegExp> MocDependFilters; - std::vector<KeyRegExp> MocMacroFilters; - cmsys::RegularExpression MocRegExpInclude; - std::vector<std::unique_ptr<MocJobIncluded>> MocJobsIncluded; - std::vector<std::unique_ptr<MocJobAuto>> MocJobsAuto; - // -- Uic - bool UicSettingsChanged; - std::vector<std::string> UicSkipList; - std::vector<std::string> UicTargetOptions; - std::map<std::string, std::vector<std::string>> UicOptions; - std::vector<std::string> UicSearchPaths; - cmsys::RegularExpression UicRegExpInclude; - std::vector<std::unique_ptr<UicJob>> UicJobs; + BaseSettingsT Base_; + MocSettingsT Moc_; + UicSettingsT Uic_; + // -- Progress + StageT Stage_; + // -- Job queues + std::mutex JobsMutex_; + struct + { + JobQueueT Sources; + JobQueueT Headers; + JobQueueT MocPredefs; + JobQueueT Moc; + JobQueueT Uic; + } JobQueues_; + JobQueueT JobQueue_; + std::size_t volatile JobsRemain_; + bool volatile JobError_; + bool volatile JobThreadsAbort_; + std::condition_variable JobsConditionRead_; + // -- Moc meta + std::set<std::string> MocIncludedStrings_; + std::set<std::string> MocIncludedFiles_; + std::set<std::string> MocAutoFiles_; + bool volatile MocAutoFileUpdated_; + // -- Settings file + std::string SettingsFile_; + std::string SettingsStringMoc_; + std::string SettingsStringUic_; + // -- Threads and loops + std::vector<std::unique_ptr<WorkerT>> Workers_; }; #endif diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx index 3c9f1a8..e8ff75a 100644 --- a/Source/cmQtAutoGeneratorRcc.cxx +++ b/Source/cmQtAutoGeneratorRcc.cxx @@ -6,22 +6,30 @@ #include "cmAlgorithms.h" #include "cmCryptoHash.h" #include "cmMakefile.h" -#include "cmOutputConverter.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" -// -- Static variables - -static const char* SettingsKeyRcc = "ARCC_SETTINGS_HASH"; +#include <functional> // -- Class methods cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc() - : MultiConfig(cmQtAutoGen::WRAP) - , SettingsChanged(false) + : SettingsChanged_(false) + , MultiConfig_(MultiConfigT::WRAPPER) + , Stage_(StageT::SETTINGS_READ) + , Error_(false) + , Generate_(false) + , BuildFileChanged_(false) { + // Initialize libuv asynchronous iteration request + UVRequest().init(*UVLoop(), &cmQtAutoGeneratorRcc::UVPollStage, this); } -bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) +cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc() +{ +} + +bool cmQtAutoGeneratorRcc::Init(cmMakefile* makefile) { // Utility lambdas auto InfoGet = [makefile](const char* key) { @@ -37,7 +45,7 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) { std::string keyConf = key; keyConf += '_'; - keyConf += this->GetInfoConfig(); + keyConf += InfoConfig(); valueConf = makefile->GetDefinition(keyConf); } if (valueConf == nullptr) { @@ -53,79 +61,180 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) }; // -- Read info file - if (!makefile->ReadListFile(this->GetInfoFile().c_str())) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "File processing failed"); + if (!makefile->ReadListFile(InfoFile().c_str())) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "File processing failed"); return false; } // -- Meta - this->MultiConfig = - cmQtAutoGen::MultiConfigType(InfoGet("ARCC_MULTI_CONFIG")); - this->ConfigSuffix = InfoGetConfig("ARCC_CONFIG_SUFFIX"); - if (this->ConfigSuffix.empty()) { - this->ConfigSuffix = "_"; - this->ConfigSuffix += this->GetInfoConfig(); + MultiConfig_ = MultiConfigType(InfoGet("ARCC_MULTI_CONFIG")); + ConfigSuffix_ = InfoGetConfig("ARCC_CONFIG_SUFFIX"); + if (ConfigSuffix_.empty()) { + ConfigSuffix_ = "_"; + ConfigSuffix_ += InfoConfig(); } - this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE"); + SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE"); // - Files and directories - this->ProjectSourceDir = InfoGet("ARCC_CMAKE_SOURCE_DIR"); - this->ProjectBinaryDir = InfoGet("ARCC_CMAKE_BINARY_DIR"); - this->CurrentSourceDir = InfoGet("ARCC_CMAKE_CURRENT_SOURCE_DIR"); - this->CurrentBinaryDir = InfoGet("ARCC_CMAKE_CURRENT_BINARY_DIR"); - this->AutogenBuildDir = InfoGet("ARCC_BUILD_DIR"); + AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR"); // - Qt environment - this->RccExecutable = InfoGet("ARCC_RCC_EXECUTABLE"); - this->RccListOptions = InfoGetList("ARCC_RCC_LIST_OPTIONS"); + RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE"); + RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS"); // - Job - this->QrcFile = InfoGet("ARCC_SOURCE"); - this->RccFile = InfoGet("ARCC_OUTPUT"); - this->Options = InfoGetConfigList("ARCC_OPTIONS"); - this->Inputs = InfoGetList("ARCC_INPUTS"); + QrcFile_ = InfoGet("ARCC_SOURCE"); + QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_); + QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_); + RccFile_ = InfoGet("ARCC_OUTPUT"); + Options_ = InfoGetConfigList("ARCC_OPTIONS"); + Inputs_ = InfoGetList("ARCC_INPUTS"); // - Validity checks - if (this->SettingsFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "Settings file name missing"); + if (SettingsFile_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Settings file name missing"); return false; } - if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "Autogen build directory missing"); + if (AutogenBuildDir_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), + "Autogen build directory missing"); return false; } - if (this->RccExecutable.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "rcc executable missing"); + if (RccExecutable_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc executable missing"); return false; } - if (this->QrcFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "rcc input file missing"); + if (QrcFile_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc input file missing"); return false; } - if (this->RccFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "rcc output file missing"); + if (RccFile_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc output file missing"); return false; } // Init derived information // ------------------------ - // Init file path checksum generator - this->FilePathChecksum.setupParentDirs( - this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, - this->ProjectBinaryDir); + // Compute rcc output file name + { + std::string suffix; + switch (MultiConfig_) { + case MultiConfigT::SINGLE: + break; + case MultiConfigT::WRAPPER: + suffix = "_CMAKE"; + suffix += ConfigSuffix_; + suffix += "_"; + break; + case MultiConfigT::MULTI: + suffix = ConfigSuffix_; + break; + } + RccFileBuild_ = AppendFilenameSuffix(RccFile_, suffix); + } + + return true; +} +bool cmQtAutoGeneratorRcc::Process() +{ + // Run libuv event loop + UVRequest().send(); + if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) { + if (Error_) { + return false; + } + } else { + return false; + } return true; } -void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile) +void cmQtAutoGeneratorRcc::UVPollStage(uv_async_t* handle) +{ + reinterpret_cast<cmQtAutoGeneratorRcc*>(handle->data)->PollStage(); +} + +void cmQtAutoGeneratorRcc::PollStage() +{ + switch (Stage_) { + // -- Initialize + case StageT::SETTINGS_READ: + SettingsFileRead(); + SetStage(StageT::TEST_QRC_RCC_FILES); + break; + + // -- Change detection + case StageT::TEST_QRC_RCC_FILES: + if (TestQrcRccFiles()) { + SetStage(StageT::GENERATE); + } else { + SetStage(StageT::TEST_RESOURCES_READ); + } + break; + case StageT::TEST_RESOURCES_READ: + if (TestResourcesRead()) { + SetStage(StageT::TEST_RESOURCES); + } + break; + case StageT::TEST_RESOURCES: + if (TestResources()) { + SetStage(StageT::GENERATE); + } else { + SetStage(StageT::TEST_INFO_FILE); + } + break; + case StageT::TEST_INFO_FILE: + TestInfoFile(); + SetStage(StageT::GENERATE_WRAPPER); + break; + + // -- Generation + case StageT::GENERATE: + GenerateParentDir(); + SetStage(StageT::GENERATE_RCC); + break; + case StageT::GENERATE_RCC: + if (GenerateRcc()) { + SetStage(StageT::GENERATE_WRAPPER); + } + break; + case StageT::GENERATE_WRAPPER: + GenerateWrapper(); + SetStage(StageT::SETTINGS_WRITE); + break; + + // -- Finalize + case StageT::SETTINGS_WRITE: + SettingsFileWrite(); + SetStage(StageT::FINISH); + break; + case StageT::FINISH: + // Clear all libuv handles + UVRequest().reset(); + // Set highest END stage manually + Stage_ = StageT::END; + break; + case StageT::END: + break; + } +} + +void cmQtAutoGeneratorRcc::SetStage(StageT stage) +{ + if (Error_) { + stage = StageT::FINISH; + } + // Only allow to increase the stage + if (Stage_ < stage) { + Stage_ = stage; + UVRequest().send(); + } +} + +void cmQtAutoGeneratorRcc::SettingsFileRead() { // Compose current settings strings { @@ -133,293 +242,375 @@ void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile) std::string const sep(" ~~~ "); { std::string str; - str += this->RccExecutable; + str += RccExecutable_; str += sep; - str += cmJoin(this->RccListOptions, ";"); + str += cmJoin(RccListOptions_, ";"); str += sep; - str += this->QrcFile; + str += QrcFile_; str += sep; - str += this->RccFile; + str += RccFile_; str += sep; - str += cmJoin(this->Options, ";"); + str += cmJoin(Options_, ";"); str += sep; - str += cmJoin(this->Inputs, ";"); + str += cmJoin(Inputs_, ";"); str += sep; - this->SettingsString = crypt.HashString(str); + SettingsString_ = crypt.HashString(str); } } // Read old settings - if (makefile->ReadListFile(this->SettingsFile.c_str())) { - { - auto SMatch = [makefile](const char* key, std::string const& value) { - return (value == makefile->GetSafeDefinition(key)); - }; - if (!SMatch(SettingsKeyRcc, this->SettingsString)) { - this->SettingsChanged = true; + { + std::string content; + if (FileSys().FileRead(content, SettingsFile_)) { + SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc")); + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (SettingsChanged_) { + FileSys().FileRemove(SettingsFile_); } + } else { + SettingsChanged_ = true; } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (this->SettingsChanged) { - cmSystemTools::RemoveFile(this->SettingsFile); - } - } else { - // If the file could not be read re-generate everythiung. - this->SettingsChanged = true; } } -bool cmQtAutoGeneratorRcc::SettingsFileWrite() +void cmQtAutoGeneratorRcc::SettingsFileWrite() { - bool success = true; // Only write if any setting changed - if (this->SettingsChanged) { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::RCC, "Writing settings file " + - cmQtAutoGen::Quoted(this->SettingsFile)); - } - // Compose settings file content - std::string settings; - { - auto SettingAppend = [&settings](const char* key, - std::string const& value) { - settings += "set("; - settings += key; - settings += " "; - settings += cmOutputConverter::EscapeForCMake(value); - settings += ")\n"; - }; - SettingAppend(SettingsKeyRcc, this->SettingsString); + if (SettingsChanged_) { + if (Log().Verbose()) { + Log().Info(GeneratorT::RCC, + "Writing settings file " + Quoted(SettingsFile_)); } // Write settings file - if (!this->FileWrite(cmQtAutoGen::RCC, this->SettingsFile, settings)) { - this->LogFileError(cmQtAutoGen::RCC, this->SettingsFile, - "Settings file writing failed"); + std::string content = "rcc:"; + content += SettingsString_; + content += '\n'; + if (!FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, content)) { + Log().ErrorFile(GeneratorT::RCC, SettingsFile_, + "Settings file writing failed"); // Remove old settings file to trigger a full rebuild on the next run - cmSystemTools::RemoveFile(this->SettingsFile); - success = false; + FileSys().FileRemove(SettingsFile_); + Error_ = true; } } - return success; } -bool cmQtAutoGeneratorRcc::Process(cmMakefile* makefile) +bool cmQtAutoGeneratorRcc::TestQrcRccFiles() { - // Read info file - if (!this->InfoFileRead(makefile)) { - return false; + // Do basic checks if rcc generation is required + + // Test if the rcc output file exists + if (!FileSys().FileExists(RccFileBuild_)) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " from its source file "; + reason += Quoted(QrcFile_); + reason += " because it doesn't exist"; + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + return Generate_; } - // Read latest settings - this->SettingsFileRead(makefile); - // Generate rcc file - if (!this->RccGenerate()) { - return false; + + // Test if the settings changed + if (SettingsChanged_) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " from "; + reason += Quoted(QrcFile_); + reason += " because the RCC settings changed"; + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + return Generate_; } - // Write latest settings - if (!this->SettingsFileWrite()) { - return false; + + // Test if the rcc output file is older than the .qrc file + { + bool isOlder = false; + { + std::string error; + isOlder = FileSys().FileIsOlderThan(RccFileBuild_, QrcFile_, &error); + if (!error.empty()) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; + } + } + if (isOlder) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " because it is older than "; + reason += Quoted(QrcFile_); + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + } } - return true; + + return Generate_; } -/** - * @return True on success - */ -bool cmQtAutoGeneratorRcc::RccGenerate() +bool cmQtAutoGeneratorRcc::TestResourcesRead() { - bool success = true; - bool rccGenerated = false; - - std::string rccFileAbs; - { - std::string suffix; - switch (this->MultiConfig) { - case cmQtAutoGen::SINGLE: - break; - case cmQtAutoGen::WRAP: - suffix = "_CMAKE"; - suffix += this->ConfigSuffix; - suffix += "_"; - break; - case cmQtAutoGen::FULL: - suffix = this->ConfigSuffix; - break; - } - rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(this->RccFile, suffix); + if (!Inputs_.empty()) { + // Inputs are known already + return true; } - std::string const rccFileRel = cmSystemTools::RelativePath( - this->AutogenBuildDir.c_str(), rccFileAbs.c_str()); - // Check if regeneration is required - bool generate = false; - std::string generateReason; - if (!cmSystemTools::FileExists(this->QrcFile)) { - { - std::string error = "Could not find the file\n "; - error += cmQtAutoGen::Quoted(this->QrcFile); - this->LogError(cmQtAutoGen::RCC, error); + if (!RccListOptions_.empty()) { + // Start a rcc list process and parse the output + if (Process_) { + // Process is running already + if (Process_->IsFinished()) { + // Process is finished + if (!ProcessResult_.error()) { + // Process success + std::string parseError; + if (!RccListParseOutput(ProcessResult_.StdOut, ProcessResult_.StdErr, + Inputs_, parseError)) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, parseError); + Error_ = true; + } + } else { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, + ProcessResult_.ErrorMessage); + Error_ = true; + } + // Clean up + Process_.reset(); + ProcessResult_.reset(); + } else { + // Process is not finished, yet. + return false; + } + } else { + // Start a new process + // rcc prints relative entry paths when started in the directory of the + // qrc file with a pathless qrc file name argument. + // This is important because on Windows absolute paths returned by rcc + // might contain bad multibyte characters when the qrc file path + // contains non-ASCII pcharacters. + std::vector<std::string> cmd; + cmd.push_back(RccExecutable_); + cmd.insert(cmd.end(), RccListOptions_.begin(), RccListOptions_.end()); + cmd.push_back(QrcFileName_); + // We're done here if the process fails to start + return !StartProcess(QrcFileDir_, cmd, false); } - success = false; - } - if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); - generateReason += " because it doesn't exist"; + } else { + // rcc does not support the --list command. + // Read the qrc file content and parse it. + std::string qrcContent; + if (FileSys().FileRead(GeneratorT::RCC, qrcContent, QrcFile_)) { + RccListParseContent(qrcContent, Inputs_); } - generate = true; } - if (success && !generate && this->SettingsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); - generateReason += " because the RCC settings changed"; - } - generate = true; + + if (!Inputs_.empty()) { + // Convert relative paths to absolute paths + RccListConvertFullPath(QrcFileDir_, Inputs_); + } + + return true; +} + +bool cmQtAutoGeneratorRcc::TestResources() +{ + if (Inputs_.empty()) { + return true; } - if (success && !generate) { + { std::string error; - if (FileIsOlderThan(rccFileAbs, this->QrcFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); + for (std::string const& resFile : Inputs_) { + // Check if the resource file exists + if (!FileSys().FileExists(resFile)) { + error = "Could not find the resource file\n "; + error += Quoted(resFile); + error += '\n'; + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; + break; } - generate = true; - } else { + // Check if the resource file is newer than the build file + if (FileSys().FileIsOlderThan(RccFileBuild_, resFile, &error)) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " from "; + reason += Quoted(QrcFile_); + reason += " because it is older than "; + reason += Quoted(resFile); + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + break; + } + // Print error and break on demand if (!error.empty()) { - this->LogError(cmQtAutoGen::RCC, error); - success = false; + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; + break; } } } - if (success && !generate) { - // Acquire input file list - std::vector<std::string> readFiles; - std::vector<std::string> const* files = nullptr; - if (!this->Inputs.empty()) { - files = &this->Inputs; - } else { - // Read input file list from qrc file + + return Generate_; +} + +void cmQtAutoGeneratorRcc::TestInfoFile() +{ + // Test if the rcc output file is older than the info file + { + bool isOlder = false; + { std::string error; - if (cmQtAutoGen::RccListInputs(this->RccExecutable, this->RccListOptions, - this->QrcFile, readFiles, &error)) { - files = &readFiles; - } else { - this->LogFileError(cmQtAutoGen::RCC, this->QrcFile, error); - success = false; + isOlder = FileSys().FileIsOlderThan(RccFileBuild_, InfoFile(), &error); + if (!error.empty()) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; } } - // Test if any input file is newer than the build file - if (files != nullptr) { - std::string error; - for (std::string const& resFile : *files) { - if (!cmSystemTools::FileExists(resFile.c_str())) { - error = "Could not find the file\n "; - error += cmQtAutoGen::Quoted(resFile); - error += "\nwhich is listed in\n "; - error += cmQtAutoGen::Quoted(this->QrcFile); - break; - } - if (FileIsOlderThan(rccFileAbs, resFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(resFile); - } - generate = true; - break; - } - if (!error.empty()) { - break; - } - } - // Print error - if (!error.empty()) { - this->LogError(cmQtAutoGen::RCC, error); - success = false; + if (isOlder) { + if (Log().Verbose()) { + std::string reason = "Touching "; + reason += Quoted(RccFileBuild_); + reason += " because it is older than "; + reason += Quoted(InfoFile()); + Log().Info(GeneratorT::RCC, reason); } + // Touch build file + FileSys().Touch(RccFileBuild_); + BuildFileChanged_ = true; } } - // Regenerate on demand - if (generate) { - // Log - if (this->GetVerbose()) { - this->LogBold("Generating RCC source " + rccFileRel); - this->LogInfo(cmQtAutoGen::RCC, generateReason); - } +} - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) { - // Compose rcc command - std::vector<std::string> cmd; - cmd.push_back(this->RccExecutable); - cmd.insert(cmd.end(), this->Options.begin(), this->Options.end()); - cmd.push_back("-o"); - cmd.push_back(rccFileAbs); - cmd.push_back(this->QrcFile); - - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - rccGenerated = true; +void cmQtAutoGeneratorRcc::GenerateParentDir() +{ + // Make sure the parent directory exists + if (!FileSys().MakeParentDirectory(GeneratorT::RCC, RccFileBuild_)) { + Error_ = true; + } +} + +/** + * @return True when finished + */ +bool cmQtAutoGeneratorRcc::GenerateRcc() +{ + if (!Generate_) { + // Nothing to do + return true; + } + + if (Process_) { + // Process is running already + if (Process_->IsFinished()) { + // Process is finished + if (!ProcessResult_.error()) { + // Process success + BuildFileChanged_ = true; } else { + // Process failed { - std::string emsg = "rcc failed for\n "; - emsg += cmQtAutoGen::Quoted(this->QrcFile); - this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output); + std::string emsg = "The rcc process failed to compile\n "; + emsg += Quoted(QrcFile_); + emsg += "\ninto\n "; + emsg += Quoted(RccFileBuild_); + if (ProcessResult_.error()) { + emsg += "\n"; + emsg += ProcessResult_.ErrorMessage; + } + Log().ErrorCommand(GeneratorT::RCC, emsg, Process_->Setup().Command, + ProcessResult_.StdOut); } - cmSystemTools::RemoveFile(rccFileAbs); - success = false; + FileSys().FileRemove(RccFileBuild_); + Error_ = true; } + // Clean up + Process_.reset(); + ProcessResult_.reset(); } else { - // Parent directory creation failed - success = false; + // Process is not finished, yet. + return false; } + } else { + // Start a rcc process + std::vector<std::string> cmd; + cmd.push_back(RccExecutable_); + cmd.insert(cmd.end(), Options_.begin(), Options_.end()); + cmd.push_back("-o"); + cmd.push_back(RccFileBuild_); + cmd.push_back(QrcFile_); + // We're done here if the process fails to start + return !StartProcess(AutogenBuildDir_, cmd, true); } + return true; +} + +void cmQtAutoGeneratorRcc::GenerateWrapper() +{ // Generate a wrapper source file on demand - if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) { + if (MultiConfig_ == MultiConfigT::WRAPPER) { // Wrapper file name - std::string const& wrapperFileAbs = this->RccFile; - std::string const wrapperFileRel = cmSystemTools::RelativePath( - this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str()); + std::string const& wrapperAbs = RccFile_; // Wrapper file content std::string content = "// This is an autogenerated configuration " "wrapper file. Changes will be overwritten.\n" "#include \""; - content += cmSystemTools::GetFilenameName(rccFileRel); + content += cmSystemTools::GetFilenameName(RccFileBuild_); content += "\"\n"; // Write content to file - if (this->FileDiffers(wrapperFileAbs, content)) { + if (FileSys().FileDiffers(wrapperAbs, content)) { // Write new wrapper file - if (this->GetVerbose()) { - this->LogBold("Generating RCC wrapper " + wrapperFileRel); + if (Log().Verbose()) { + Log().Info(GeneratorT::RCC, "Generating RCC wrapper " + wrapperAbs); } - if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) { - this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs, - "rcc wrapper file writing failed"); - success = false; + if (!FileSys().FileWrite(GeneratorT::RCC, wrapperAbs, content)) { + Log().ErrorFile(GeneratorT::RCC, wrapperAbs, + "RCC wrapper file writing failed"); + Error_ = true; } - } else if (rccGenerated) { + } else if (BuildFileChanged_) { // Just touch the wrapper file - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::RCC, - "Touching RCC wrapper " + wrapperFileRel); + if (Log().Verbose()) { + Log().Info(GeneratorT::RCC, "Touching RCC wrapper " + wrapperAbs); } - cmSystemTools::Touch(wrapperFileAbs, false); + FileSys().Touch(wrapperAbs); } } +} + +bool cmQtAutoGeneratorRcc::StartProcess( + std::string const& workingDirectory, std::vector<std::string> const& command, + bool mergedOutput) +{ + // Log command + if (Log().Verbose()) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(command); + msg += '\n'; + Log().Info(GeneratorT::RCC, msg); + } - return success; + // Create process handler + Process_ = cm::make_unique<ReadOnlyProcessT>(); + Process_->setup(&ProcessResult_, mergedOutput, command, workingDirectory); + // Start process + if (!Process_->start(UVLoop(), + std::bind(&cm::uv_async_ptr::send, &UVRequest()))) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, ProcessResult_.ErrorMessage); + Error_ = true; + // Clean up + Process_.reset(); + ProcessResult_.reset(); + return false; + } + return true; } diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoGeneratorRcc.h index 0e3f690..8a69c6c 100644 --- a/Source/cmQtAutoGeneratorRcc.h +++ b/Source/cmQtAutoGeneratorRcc.h @@ -5,52 +5,97 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" +#include "cm_uv.h" #include <string> #include <vector> class cmMakefile; +// @brief AUTORCC generator class cmQtAutoGeneratorRcc : public cmQtAutoGenerator { CM_DISABLE_COPY(cmQtAutoGeneratorRcc) public: cmQtAutoGeneratorRcc(); + ~cmQtAutoGeneratorRcc() override; private: - // -- Initialization & settings - bool InfoFileRead(cmMakefile* makefile); - void SettingsFileRead(cmMakefile* makefile); - bool SettingsFileWrite(); - // -- Central processing - bool Process(cmMakefile* makefile) override; - bool RccGenerate(); + // -- Types + /// @brief Processing stage + enum class StageT + { + SETTINGS_READ, + TEST_QRC_RCC_FILES, + TEST_RESOURCES_READ, + TEST_RESOURCES, + TEST_INFO_FILE, + GENERATE, + GENERATE_RCC, + GENERATE_WRAPPER, + SETTINGS_WRITE, + FINISH, + END + }; + + // -- Abstract processing interface + bool Init(cmMakefile* makefile) override; + bool Process() override; + // -- Process stage + static void UVPollStage(uv_async_t* handle); + void PollStage(); + void SetStage(StageT stage); + // -- Settings file + void SettingsFileRead(); + void SettingsFileWrite(); + // -- Tests + bool TestQrcRccFiles(); + bool TestResourcesRead(); + bool TestResources(); + void TestInfoFile(); + // -- Generation + void GenerateParentDir(); + bool GenerateRcc(); + void GenerateWrapper(); + + // -- Utility + bool StartProcess(std::string const& workingDirectory, + std::vector<std::string> const& command, + bool mergedOutput); + +private: // -- Config settings - std::string ConfigSuffix; - cmQtAutoGen::MultiConfig MultiConfig; - // -- Settings - bool SettingsChanged; - std::string SettingsFile; - std::string SettingsString; + bool SettingsChanged_; + std::string ConfigSuffix_; + MultiConfigT MultiConfig_; // -- Directories - std::string ProjectSourceDir; - std::string ProjectBinaryDir; - std::string CurrentSourceDir; - std::string CurrentBinaryDir; - std::string AutogenBuildDir; - cmFilePathChecksum FilePathChecksum; + std::string AutogenBuildDir_; // -- Qt environment - std::string RccExecutable; - std::vector<std::string> RccListOptions; + std::string RccExecutable_; + std::vector<std::string> RccListOptions_; // -- Job - std::string QrcFile; - std::string RccFile; - std::vector<std::string> Options; - std::vector<std::string> Inputs; + std::string QrcFile_; + std::string QrcFileName_; + std::string QrcFileDir_; + std::string RccFile_; + std::string RccFileWrapper_; + std::string RccFileBuild_; + std::vector<std::string> Options_; + std::vector<std::string> Inputs_; + // -- Subprocess + ProcessResultT ProcessResult_; + std::unique_ptr<ReadOnlyProcessT> Process_; + // -- Settings file + std::string SettingsFile_; + std::string SettingsString_; + // -- libuv loop + StageT Stage_; + bool Error_; + bool Generate_; + bool BuildFileChanged_; }; #endif diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 6b7143b..4c0a354 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -1034,8 +1034,8 @@ static Json::Value DumpProjectList(const cmake* cm, std::string const& config) // Project structure information: const cmMakefile* mf = lg->GetMakefile(); - pObj[kMINIMUM_CMAKE_VERSION] = - mf->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); + auto minVersion = mf->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); + pObj[kMINIMUM_CMAKE_VERSION] = minVersion ? minVersion : ""; pObj[kSOURCE_DIRECTORY_KEY] = mf->GetCurrentSourceDirectory(); pObj[kBUILD_DIRECTORY_KEY] = mf->GetCurrentBinaryDirectory(); pObj[kTARGETS_KEY] = DumpTargetsList(projectIt.second, config); diff --git a/Source/cmSourceGroup.cxx b/Source/cmSourceGroup.cxx index 18bcb49..12ef62b 100644 --- a/Source/cmSourceGroup.cxx +++ b/Source/cmSourceGroup.cxx @@ -8,7 +8,7 @@ public: std::vector<cmSourceGroup> GroupChildren; }; -cmSourceGroup::cmSourceGroup(const char* name, const char* regex, +cmSourceGroup::cmSourceGroup(const std::string& name, const char* regex, const char* parentName) : Name(name) { @@ -70,14 +70,14 @@ std::string const& cmSourceGroup::GetFullName() const return this->FullName; } -bool cmSourceGroup::MatchesRegex(const char* name) +bool cmSourceGroup::MatchesRegex(const std::string& name) { return this->GroupRegex.find(name); } -bool cmSourceGroup::MatchesFiles(const char* name) +bool cmSourceGroup::MatchesFiles(const std::string& name) const { - return this->GroupFiles.find(name) != this->GroupFiles.end(); + return this->GroupFiles.find(name) != this->GroupFiles.cend(); } void cmSourceGroup::AssignSource(const cmSourceFile* sf) @@ -95,21 +95,12 @@ void cmSourceGroup::AddChild(cmSourceGroup const& child) this->Internal->GroupChildren.push_back(child); } -cmSourceGroup* cmSourceGroup::LookupChild(const char* name) const +cmSourceGroup* cmSourceGroup::LookupChild(const std::string& name) { - // initializing iterators - std::vector<cmSourceGroup>::const_iterator iter = - this->Internal->GroupChildren.begin(); - const std::vector<cmSourceGroup>::const_iterator end = - this->Internal->GroupChildren.end(); - - // st - for (; iter != end; ++iter) { - std::string const& sgName = iter->GetName(); - + for (cmSourceGroup& group : this->Internal->GroupChildren) { // look if descenened is the one were looking for - if (sgName == name) { - return const_cast<cmSourceGroup*>(&(*iter)); // if it so return it + if (group.GetName() == name) { + return (&group); // if it so return it } } @@ -117,19 +108,13 @@ cmSourceGroup* cmSourceGroup::LookupChild(const char* name) const return nullptr; } -cmSourceGroup* cmSourceGroup::MatchChildrenFiles(const char* name) +cmSourceGroup* cmSourceGroup::MatchChildrenFiles(const std::string& name) { - // initializing iterators - std::vector<cmSourceGroup>::iterator iter = - this->Internal->GroupChildren.begin(); - std::vector<cmSourceGroup>::iterator end = - this->Internal->GroupChildren.end(); - if (this->MatchesFiles(name)) { return this; } - for (; iter != end; ++iter) { - cmSourceGroup* result = iter->MatchChildrenFiles(name); + for (cmSourceGroup& group : this->Internal->GroupChildren) { + cmSourceGroup* result = group.MatchChildrenFiles(name); if (result) { return result; } @@ -137,16 +122,10 @@ cmSourceGroup* cmSourceGroup::MatchChildrenFiles(const char* name) return nullptr; } -cmSourceGroup* cmSourceGroup::MatchChildrenRegex(const char* name) +cmSourceGroup* cmSourceGroup::MatchChildrenRegex(const std::string& name) { - // initializing iterators - std::vector<cmSourceGroup>::iterator iter = - this->Internal->GroupChildren.begin(); - std::vector<cmSourceGroup>::iterator end = - this->Internal->GroupChildren.end(); - - for (; iter != end; ++iter) { - cmSourceGroup* result = iter->MatchChildrenRegex(name); + for (cmSourceGroup& group : this->Internal->GroupChildren) { + cmSourceGroup* result = group.MatchChildrenRegex(name); if (result) { return result; } diff --git a/Source/cmSourceGroup.h b/Source/cmSourceGroup.h index 7c7c35f..b39f8dd 100644 --- a/Source/cmSourceGroup.h +++ b/Source/cmSourceGroup.h @@ -26,7 +26,7 @@ class cmSourceGroupInternals; class cmSourceGroup { public: - cmSourceGroup(const char* name, const char* regex, + cmSourceGroup(const std::string& name, const char* regex, const char* parentName = nullptr); cmSourceGroup(cmSourceGroup const& r); ~cmSourceGroup(); @@ -50,7 +50,7 @@ public: /** * Looks up child and returns it */ - cmSourceGroup* LookupChild(const char* name) const; + cmSourceGroup* LookupChild(const std::string& name); /** * Get the name of this group. @@ -65,23 +65,23 @@ public: /** * Check if the given name matches this group's regex. */ - bool MatchesRegex(const char* name); + bool MatchesRegex(const std::string& name); /** * Check if the given name matches this group's explicit file list. */ - bool MatchesFiles(const char* name); + bool MatchesFiles(const std::string& name) const; /** * Check if the given name matches this group's explicit file list * in children. */ - cmSourceGroup* MatchChildrenFiles(const char* name); + cmSourceGroup* MatchChildrenFiles(const std::string& name); /** * Check if the given name matches this group's regex in children. */ - cmSourceGroup* MatchChildrenRegex(const char* name); + cmSourceGroup* MatchChildrenRegex(const std::string& name); /** * Assign the given source file to this group. Used only by diff --git a/Source/cmState.cxx b/Source/cmState.cxx index 5957b5b..00d7e9a 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -107,9 +107,9 @@ bool cmState::LoadCache(const std::string& path, bool internal, return this->CacheManager->LoadCache(path, internal, excludes, includes); } -bool cmState::SaveCache(const std::string& path) +bool cmState::SaveCache(const std::string& path, cmMessenger* messenger) { - return this->CacheManager->SaveCache(path); + return this->CacheManager->SaveCache(path, messenger); } bool cmState::DeleteCache(const std::string& path) diff --git a/Source/cmState.h b/Source/cmState.h index e03ad89..7282f0a 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -23,6 +23,7 @@ class cmCacheManager; class cmCommand; class cmPropertyDefinition; class cmStateSnapshot; +class cmMessenger; class cmState { @@ -59,7 +60,7 @@ public: std::set<std::string>& excludes, std::set<std::string>& includes); - bool SaveCache(const std::string& path); + bool SaveCache(const std::string& path, cmMessenger* messenger); bool DeleteCache(const std::string& path); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 1974be3..663a4c9 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -247,6 +247,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("AUTOMOC", nullptr); this->SetPropertyDefault("AUTOUIC", nullptr); this->SetPropertyDefault("AUTORCC", nullptr); + this->SetPropertyDefault("AUTOGEN_PARALLEL", nullptr); this->SetPropertyDefault("AUTOMOC_COMPILER_PREDEFINES", nullptr); this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", nullptr); this->SetPropertyDefault("AUTOMOC_MACRO_NAMES", nullptr); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index ee9db43..1b09600 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2319,6 +2319,18 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental( } } +std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes( + std::string const& config, std::string const& lang) const +{ + std::vector<std::string> includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, + lang, config); + for (std::string& i : includes) { + this->ConvertToWindowsSlash(i); + } + return includes; +} + bool cmVisualStudio10TargetGenerator::ComputeClOptions() { for (std::string const& i : this->Configurations) { @@ -2379,6 +2391,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } } } + this->LangForClCompile = langForClCompile; if (!langForClCompile.empty()) { std::string baseFlagVar = "CMAKE_"; baseFlagVar += langForClCompile; @@ -2422,8 +2435,10 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( std::vector<std::string> targetDefines; switch (this->ProjectType) { case vcxproj: - this->GeneratorTarget->GetCompileDefinitions(targetDefines, configName, - "CXX"); + if (!langForClCompile.empty()) { + this->GeneratorTarget->GetCompileDefinitions(targetDefines, configName, + langForClCompile); + } break; case csproj: this->GeneratorTarget->GetCompileDefinitions(targetDefines, configName, @@ -2487,7 +2502,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } void cmVisualStudio10TargetGenerator::WriteClOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { Options& clOptions = *(this->ClOptions[configName]); if (this->ProjectType == csproj) { @@ -2495,12 +2510,16 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( } this->WriteString("<ClCompile>\n", 2); clOptions.PrependInheritedString("AdditionalOptions"); - clOptions.AppendFlag("AdditionalIncludeDirectories", includes); + if (!this->LangForClCompile.empty()) { + std::vector<std::string> const includes = + this->GetIncludes(configName, this->LangForClCompile); + clOptions.AppendFlag("AdditionalIncludeDirectories", includes); + } clOptions.AppendFlag("AdditionalIncludeDirectories", "%(AdditionalIncludeDirectories)"); clOptions.OutputFlagMap(*this->BuildFileStream, " "); clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", "CXX"); + "\n", this->LangForClCompile); if (this->NsightTegra) { if (const char* processMax = @@ -2581,7 +2600,7 @@ bool cmVisualStudio10TargetGenerator::ComputeRcOptions( } void cmVisualStudio10TargetGenerator::WriteRCOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools) { return; @@ -2591,6 +2610,8 @@ void cmVisualStudio10TargetGenerator::WriteRCOptions( Options& rcOptions = *(this->RcOptions[configName]); rcOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", "\n", "RC"); + std::vector<std::string> const includes = + this->GetIncludes(configName, "RC"); rcOptions.AppendFlag("AdditionalIncludeDirectories", includes); rcOptions.AppendFlag("AdditionalIncludeDirectories", "%(AdditionalIncludeDirectories)"); @@ -2646,6 +2667,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( cudaOptions.AddFlag("GPUDebugInfo", "false"); } + bool notPtx = true; if (this->GeneratorTarget->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION")) { cudaOptions.AddFlag("GenerateRelocatableDeviceCode", "true"); } else if (this->GeneratorTarget->GetPropertyAsBool( @@ -2654,6 +2676,16 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( // We drop the %(Extension) component as CMake expects all PTX files // to not have the source file extension at all cudaOptions.AddFlag("CompileOut", "$(IntDir)%(Filename).ptx"); + notPtx = false; + } + + if (notPtx && + cmSystemTools::VersionCompareGreaterEq( + "8.0", this->GlobalGenerator->GetPlatformToolsetCudaString())) { + // Explicitly state that we want this file to be treated as a + // CUDA file no matter what the file extensions is + // This is only needed for < CUDA 9 + cudaOptions.AppendFlagString("AdditionalOptions", "-x cu"); } // CUDA automatically passes the proper '--machine' flag to nvcc @@ -2701,7 +2733,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( } void cmVisualStudio10TargetGenerator::WriteCudaOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools || !this->GlobalGenerator->IsCudaEnabled()) { return; @@ -2709,6 +2741,8 @@ void cmVisualStudio10TargetGenerator::WriteCudaOptions( this->WriteString("<CudaCompile>\n", 2); Options& cudaOptions = *(this->CudaOptions[configName]); + std::vector<std::string> const includes = + this->GetIncludes(configName, "CUDA"); cudaOptions.AppendFlag("Include", includes); cudaOptions.AppendFlag("Include", "%(Include)"); cudaOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", @@ -2823,7 +2857,7 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions( } void cmVisualStudio10TargetGenerator::WriteMasmOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools || !this->GlobalGenerator->IsMasmEnabled()) { return; @@ -2836,6 +2870,8 @@ void cmVisualStudio10TargetGenerator::WriteMasmOptions( "\n", "ASM_MASM"); Options& masmOptions = *(this->MasmOptions[configName]); + std::vector<std::string> const includes = + this->GetIncludes(configName, "ASM_MASM"); masmOptions.AppendFlag("IncludePaths", includes); masmOptions.AppendFlag("IncludePaths", "%(IncludePaths)"); masmOptions.PrependInheritedString("AdditionalOptions"); @@ -2880,13 +2916,15 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions( } void cmVisualStudio10TargetGenerator::WriteNasmOptions( - std::string const& configName, std::vector<std::string> includes) + std::string const& configName) { if (!this->GlobalGenerator->IsNasmEnabled()) { return; } this->WriteString("<NASM>\n", 2); + std::vector<std::string> includes = + this->GetIncludes(configName, "ASM_NASM"); Options& nasmOptions = *(this->NasmOptions[configName]); for (size_t i = 0; i < includes.size(); i++) { includes[i] += "\\"; @@ -3434,7 +3472,7 @@ void cmVisualStudio10TargetGenerator::AddTargetsFileAndConfigPair( } void cmVisualStudio10TargetGenerator::WriteMidlOptions( - std::string const& /*config*/, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools) { return; @@ -3460,6 +3498,8 @@ void cmVisualStudio10TargetGenerator::WriteMidlOptions( // on the CMake side? this->WriteString("<Midl>\n", 2); this->WriteString("<AdditionalIncludeDirectories>", 3); + std::vector<std::string> const includes = + this->GetIncludes(configName, "MIDL"); for (std::string const& i : includes) { *this->BuildFileStream << cmVS10EscapeXML(i) << ";"; } @@ -3484,25 +3524,19 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() return; } for (std::string const& i : this->Configurations) { - std::vector<std::string> includes; - this->LocalGenerator->GetIncludeDirectories(includes, - this->GeneratorTarget, "C", i); - for (std::string& ii : includes) { - this->ConvertToWindowsSlash(ii); - } this->WritePlatformConfigTag("ItemDefinitionGroup", i, 1); *this->BuildFileStream << "\n"; // output cl compile flags <ClCompile></ClCompile> if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) { - this->WriteClOptions(i, includes); + this->WriteClOptions(i); // output rc compile flags <ResourceCompile></ResourceCompile> - this->WriteRCOptions(i, includes); - this->WriteCudaOptions(i, includes); - this->WriteMasmOptions(i, includes); - this->WriteNasmOptions(i, includes); + this->WriteRCOptions(i); + this->WriteCudaOptions(i); + this->WriteMasmOptions(i); + this->WriteNasmOptions(i); } // output midl flags <Midl></Midl> - this->WriteMidlOptions(i, includes); + this->WriteMidlOptions(i); // write events if (this->ProjectType != csproj) { this->WriteEvents(i); diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index fb24f1a..c346164 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -90,18 +90,18 @@ private: void WriteTargetSpecificReferences(); void WriteTargetsFileReferences(); + std::vector<std::string> GetIncludes(std::string const& config, + std::string const& lang) const; + bool ComputeClOptions(); bool ComputeClOptions(std::string const& configName); - void WriteClOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteClOptions(std::string const& config); bool ComputeRcOptions(); bool ComputeRcOptions(std::string const& config); - void WriteRCOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteRCOptions(std::string const& config); bool ComputeCudaOptions(); bool ComputeCudaOptions(std::string const& config); - void WriteCudaOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteCudaOptions(std::string const& config); bool ComputeCudaLinkOptions(); bool ComputeCudaLinkOptions(std::string const& config); @@ -109,20 +109,17 @@ private: bool ComputeMasmOptions(); bool ComputeMasmOptions(std::string const& config); - void WriteMasmOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteMasmOptions(std::string const& config); bool ComputeNasmOptions(); bool ComputeNasmOptions(std::string const& config); - void WriteNasmOptions(std::string const& config, - std::vector<std::string> includes); + void WriteNasmOptions(std::string const& config); bool ComputeLinkOptions(); bool ComputeLinkOptions(std::string const& config); bool ComputeLibOptions(); bool ComputeLibOptions(std::string const& config); void WriteLinkOptions(std::string const& config); - void WriteMidlOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteMidlOptions(std::string const& config); void WriteAntBuildOptions(std::string const& config); void OutputLinkIncremental(std::string const& configName); void WriteCustomRule(cmSourceFile const* source, @@ -180,6 +177,7 @@ private: OptionsMap MasmOptions; OptionsMap NasmOptions; OptionsMap LinkOptions; + std::string LangForClCompile; std::string PathToProjectFile; std::string ProjectFileExtension; enum VsProjectType diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 2341dd6..480646e 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1771,7 +1771,7 @@ bool cmake::LoadCache(const std::string& path, bool internal, bool cmake::SaveCache(const std::string& path) { - bool result = this->State->SaveCache(path); + bool result = this->State->SaveCache(path, this->GetMessenger()); static const char* entries[] = { "CMAKE_CACHE_MAJOR_VERSION", "CMAKE_CACHE_MINOR_VERSION", "CMAKE_CACHE_PATCH_VERSION", diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index f660f43..2c3bda2 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -103,7 +103,7 @@ void CMakeCommandUsage(const char* program) << " sleep <number>... - sleep for given number of seconds\n" << " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n" << " - create or extract a tar or zip archive\n" - << " time command [args...] - run command and return elapsed time\n" + << " time command [args...] - run command and display elapsed time\n" << " touch file - touch a file.\n" << " touch_nocreate file - touch a file but do not create it.\n" #if defined(_WIN32) && !defined(__CYGWIN__) diff --git a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt index f96283d..7dc7995 100644 --- a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt @@ -26,18 +26,18 @@ target_compile_definitions(consumer PRIVATE ) -if (CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja") - target_sources(consumer PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" - ) - target_compile_definitions(consumer - PRIVATE - CONSUMER_LANG_$<COMPILE_LANGUAGE> - LANG_IS_CXX=$<COMPILE_LANGUAGE:CXX> - LANG_IS_C=$<COMPILE_LANGUAGE:C> - ) +target_sources(consumer PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" +) +target_compile_definitions(consumer + PRIVATE + CONSUMER_LANG_$<COMPILE_LANGUAGE> + LANG_IS_CXX=$<COMPILE_LANGUAGE:CXX> + LANG_IS_C=$<COMPILE_LANGUAGE:C> +) +if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode") target_compile_definitions(consumer - PRIVATE -DTEST_LANG_DEFINES + PRIVATE TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE ) endif() diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.c b/Tests/CMakeCommands/target_compile_definitions/consumer.c index 7931a6f..e134a8b 100644 --- a/Tests/CMakeCommands/target_compile_definitions/consumer.c +++ b/Tests/CMakeCommands/target_compile_definitions/consumer.c @@ -1,5 +1,23 @@ -#ifdef TEST_LANG_DEFINES +// Visual Studio allows only one set of flags for C and C++. +// In a target using C++ we pick the C++ flags even for C sources. +#ifdef TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE +#ifndef CONSUMER_LANG_CXX +#error Expected CONSUMER_LANG_CXX +#endif + +#ifdef CONSUMER_LANG_C +#error Unexpected CONSUMER_LANG_C +#endif + +#if !LANG_IS_CXX +#error Expected LANG_IS_CXX +#endif + +#if LANG_IS_C +#error Unexpected LANG_IS_C +#endif +#else #ifdef CONSUMER_LANG_CXX #error Unexpected CONSUMER_LANG_CXX #endif diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.cpp b/Tests/CMakeCommands/target_compile_definitions/consumer.cpp index 0202c17..69ea151 100644 --- a/Tests/CMakeCommands/target_compile_definitions/consumer.cpp +++ b/Tests/CMakeCommands/target_compile_definitions/consumer.cpp @@ -15,7 +15,6 @@ #error Expected DASH_D_DEFINE #endif -#ifdef TEST_LANG_DEFINES #ifndef CONSUMER_LANG_CXX #error Expected CONSUMER_LANG_CXX #endif @@ -31,7 +30,6 @@ #if LANG_IS_C #error Unexpected LANG_IS_C #endif -#endif int main() { diff --git a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt index d57556a..8713d99 100644 --- a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt +++ b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt @@ -42,17 +42,17 @@ add_executable(consumer "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp" ) -if (CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja") - target_sources(consumer PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" - ) - target_include_directories(consumer - PRIVATE - $<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/cxx_only> - $<$<COMPILE_LANGUAGE:C>:${CMAKE_CURRENT_SOURCE_DIR}/c_only> - ) +target_sources(consumer PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" +) +target_include_directories(consumer + PRIVATE + $<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/cxx_only> + $<$<COMPILE_LANGUAGE:C>:${CMAKE_CURRENT_SOURCE_DIR}/c_only> +) +if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode") target_compile_definitions(consumer - PRIVATE -DTEST_LANG_DEFINES + PRIVATE TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE ) endif() diff --git a/Tests/CMakeCommands/target_include_directories/consumer.c b/Tests/CMakeCommands/target_include_directories/consumer.c index ae88f92..419c2d2 100644 --- a/Tests/CMakeCommands/target_include_directories/consumer.c +++ b/Tests/CMakeCommands/target_include_directories/consumer.c @@ -1,5 +1,13 @@ -#ifdef TEST_LANG_DEFINES +// Visual Studio allows only one set of flags for C and C++. +// In a target using C++ we pick the C++ flags even for C sources. +#ifdef TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE +#include "cxx_only.h" + +#ifndef CXX_ONLY_DEFINE +#error Expected CXX_ONLY_DEFINE +#endif +#else #include "c_only.h" #ifndef C_ONLY_DEFINE diff --git a/Tests/CMakeCommands/target_include_directories/consumer.cpp b/Tests/CMakeCommands/target_include_directories/consumer.cpp index 0f8153b..1e018ad 100644 --- a/Tests/CMakeCommands/target_include_directories/consumer.cpp +++ b/Tests/CMakeCommands/target_include_directories/consumer.cpp @@ -1,12 +1,10 @@ #include "consumer.h" #include "common.h" +#include "cxx_only.h" #include "interfaceinclude.h" #include "publicinclude.h" #include "relative_dir.h" -#ifdef TEST_LANG_DEFINES -#include "cxx_only.h" -#endif #ifdef PRIVATEINCLUDE_DEFINE #error Unexpected PRIVATEINCLUDE_DEFINE @@ -32,11 +30,9 @@ #error Expected CONSUMER_DEFINE #endif -#ifdef TEST_LANG_DEFINES #ifndef CXX_ONLY_DEFINE #error Expected CXX_ONLY_DEFINE #endif -#endif int main() { diff --git a/Tests/CudaOnly/WithDefs/CMakeLists.txt b/Tests/CudaOnly/WithDefs/CMakeLists.txt index 5bd93a4..926d9ed 100644 --- a/Tests/CudaOnly/WithDefs/CMakeLists.txt +++ b/Tests/CudaOnly/WithDefs/CMakeLists.txt @@ -21,19 +21,13 @@ set(release_compile_defs DEFREL) #this verifies we can pass things such as '_','(' to nvcc add_definitions("-DPACKED_DEFINE=__attribute__((packed))") -if(CMAKE_GENERATOR MATCHES "Visual Studio") - # CUDA MSBuild rules do not pass '-x cu' to nvcc - set(main main_for_vs.cu) -else() - set(main main.notcu) - set_source_files_properties(main.notcu PROPERTIES LANGUAGE CUDA) -endif() -add_executable(CudaOnlyWithDefs ${main}) +add_executable(CudaOnlyWithDefs main.notcu) +set_source_files_properties(main.notcu PROPERTIES LANGUAGE CUDA) target_compile_options(CudaOnlyWithDefs PRIVATE - -DCOMPILE_LANG_$<COMPILE_LANGUAGE> - -DLANG_IS_CUDA=$<COMPILE_LANGUAGE:CUDA> + -DFLAG_COMPILE_LANG_$<COMPILE_LANGUAGE> + -DFLAG_LANG_IS_CUDA=$<COMPILE_LANGUAGE:CUDA> -Xcompiler=-DHOST_DEFINE $<$<CONFIG:DEBUG>:$<BUILD_INTERFACE:${debug_compile_flags}>> ) @@ -41,8 +35,15 @@ target_compile_options(CudaOnlyWithDefs target_compile_definitions(CudaOnlyWithDefs PRIVATE $<$<CONFIG:RELEASE>:$<BUILD_INTERFACE:${release_compile_defs}>> + -DDEF_COMPILE_LANG_$<COMPILE_LANGUAGE> + -DDEF_LANG_IS_CUDA=$<COMPILE_LANGUAGE:CUDA> ) +target_include_directories(CudaOnlyWithDefs + PRIVATE + $<$<COMPILE_LANGUAGE:CUDA>:${CMAKE_CURRENT_SOURCE_DIR}/inc_cuda> +) + if(APPLE) # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime. set_property(TARGET CudaOnlyWithDefs PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) diff --git a/Tests/CudaOnly/WithDefs/inc_cuda/inc_cuda.h b/Tests/CudaOnly/WithDefs/inc_cuda/inc_cuda.h new file mode 100644 index 0000000..e228b58 --- /dev/null +++ b/Tests/CudaOnly/WithDefs/inc_cuda/inc_cuda.h @@ -0,0 +1 @@ +#define INC_CUDA diff --git a/Tests/CudaOnly/WithDefs/main.notcu b/Tests/CudaOnly/WithDefs/main.notcu index bfb3577..3793d74 100644 --- a/Tests/CudaOnly/WithDefs/main.notcu +++ b/Tests/CudaOnly/WithDefs/main.notcu @@ -2,6 +2,11 @@ #include <cuda_runtime.h> #include <iostream> +#include <inc_cuda.h> +#ifndef INC_CUDA +#error "INC_CUDA not defined!" +#endif + #ifndef HOST_DEFINE #error "HOST_DEFINE not defined!" #endif @@ -10,16 +15,28 @@ #error "PACKED_DEFINE not defined!" #endif -#ifndef COMPILE_LANG_CUDA -#error "COMPILE_LANG_CUDA not defined!" +#ifndef FLAG_COMPILE_LANG_CUDA +#error "FLAG_COMPILE_LANG_CUDA not defined!" +#endif + +#ifndef FLAG_LANG_IS_CUDA +#error "FLAG_LANG_IS_CUDA not defined!" +#endif + +#if !FLAG_LANG_IS_CUDA +#error "Expected FLAG_LANG_IS_CUDA" +#endif + +#ifndef DEF_COMPILE_LANG_CUDA +#error "DEF_COMPILE_LANG_CUDA not defined!" #endif -#ifndef LANG_IS_CUDA -#error "LANG_IS_CUDA not defined!" +#ifndef DEF_LANG_IS_CUDA +#error "DEF_LANG_IS_CUDA not defined!" #endif -#if !LANG_IS_CUDA -#error "Expected LANG_IS_CUDA" +#if !DEF_LANG_IS_CUDA +#error "Expected DEF_LANG_IS_CUDA" #endif static __global__ void DetermineIfValidCudaDevice() diff --git a/Tests/CudaOnly/WithDefs/main_for_vs.cu b/Tests/CudaOnly/WithDefs/main_for_vs.cu deleted file mode 100644 index 56078e7..0000000 --- a/Tests/CudaOnly/WithDefs/main_for_vs.cu +++ /dev/null @@ -1 +0,0 @@ -#include "main.notcu" diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index 0585d0c..4586357 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -269,10 +269,8 @@ set_property(SOURCE srcgenex_flags_COMPILE_LANGUAGE.c PROPERTY COMPILE_FLAGS "$< add_executable(srcgenex_defs srcgenex_defs.c) set_property(SOURCE srcgenex_defs.c PROPERTY COMPILE_DEFINITIONS NAME=$<TARGET_PROPERTY:NAME>) -if (CMAKE_GENERATOR MATCHES "Makefiles|Ninja|Watcom WMake") - add_executable(srcgenex_defs_COMPILE_LANGUAGE srcgenex_defs_COMPILE_LANGUAGE.c) - set_property(SOURCE srcgenex_defs_COMPILE_LANGUAGE.c PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:C>:NAME=$<TARGET_PROPERTY:NAME>>) -endif() +add_executable(srcgenex_defs_COMPILE_LANGUAGE srcgenex_defs_COMPILE_LANGUAGE.c) +set_property(SOURCE srcgenex_defs_COMPILE_LANGUAGE.c PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:C>:NAME=$<TARGET_PROPERTY:NAME>>) #----------------------------------------------------------------------------- # Cover test properties with generator expressions. diff --git a/Tests/QtAutogen/CommonTests.cmake b/Tests/QtAutogen/CommonTests.cmake index c56780e..2c2e6d6 100644 --- a/Tests/QtAutogen/CommonTests.cmake +++ b/Tests/QtAutogen/CommonTests.cmake @@ -29,6 +29,12 @@ ADD_AUTOGEN_TEST(ObjectLibrary someProgram) if(APPLE AND (NOT QT_TEST_VERSION STREQUAL 4)) ADD_AUTOGEN_TEST(MacOsFW) endif() +ADD_AUTOGEN_TEST(Parallel parallel) +ADD_AUTOGEN_TEST(Parallel1 parallel1) +ADD_AUTOGEN_TEST(Parallel2 parallel2) +ADD_AUTOGEN_TEST(Parallel3 parallel3) +ADD_AUTOGEN_TEST(Parallel4 parallel4) +ADD_AUTOGEN_TEST(ParallelAUTO parallelAUTO) ADD_AUTOGEN_TEST(SameName sameName) ADD_AUTOGEN_TEST(StaticLibraryCycle slc) ADD_AUTOGEN_TEST(Complex QtAutogen) diff --git a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp b/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp index dea6cb5..d8bf284 100644 --- a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp +++ b/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJAEXTRA_P_HPP #define EOBJAEXTRA_P_HPP +#include <QObject> + class EObjAExtraPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/EObjA_p.hpp b/Tests/QtAutogen/MocInclude/EObjA_p.hpp index 1e0d7e1..9ef5624 100644 --- a/Tests/QtAutogen/MocInclude/EObjA_p.hpp +++ b/Tests/QtAutogen/MocInclude/EObjA_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJA_P_HPP #define EOBJA_P_HPP +#include <QObject> + class EObjAPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/EObjB_p.hpp b/Tests/QtAutogen/MocInclude/EObjB_p.hpp index 2905f28..84b1ea2 100644 --- a/Tests/QtAutogen/MocInclude/EObjB_p.hpp +++ b/Tests/QtAutogen/MocInclude/EObjB_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJB_P_HPP #define EOBJB_P_HPP +#include <QObject> + class EObjBPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/LObjA_p.h b/Tests/QtAutogen/MocInclude/LObjA_p.h index ebe8395..97113d6 100644 --- a/Tests/QtAutogen/MocInclude/LObjA_p.h +++ b/Tests/QtAutogen/MocInclude/LObjA_p.h @@ -1,6 +1,8 @@ #ifndef LOBJA_P_HPP #define LOBJA_P_HPP +#include <QObject> + class LObjAPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/LObjB_p.h b/Tests/QtAutogen/MocInclude/LObjB_p.h index b871f2d..b88f40e 100644 --- a/Tests/QtAutogen/MocInclude/LObjB_p.h +++ b/Tests/QtAutogen/MocInclude/LObjB_p.h @@ -1,6 +1,8 @@ #ifndef LOBJB_P_HPP #define LOBJB_P_HPP +#include <QObject> + class LObjBPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/ObjA_p.h b/Tests/QtAutogen/MocInclude/ObjA_p.h index eb60c98..d944bc6 100644 --- a/Tests/QtAutogen/MocInclude/ObjA_p.h +++ b/Tests/QtAutogen/MocInclude/ObjA_p.h @@ -1,6 +1,8 @@ #ifndef OBJA_P_HPP #define OBJA_P_HPP +#include <QObject> + class ObjAPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/ObjB_p.h b/Tests/QtAutogen/MocInclude/ObjB_p.h index 418da65..61ba604 100644 --- a/Tests/QtAutogen/MocInclude/ObjB_p.h +++ b/Tests/QtAutogen/MocInclude/ObjB_p.h @@ -1,6 +1,8 @@ #ifndef OBJB_P_HPP #define OBJB_P_HPP +#include <QObject> + class ObjBPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp index db8a096..3231fac 100644 --- a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp +++ b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJBEXTRA_P_HPP #define EOBJBEXTRA_P_HPP +#include <QObject> + class EObjBExtraPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp b/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp index 7b37dfd..4a43755 100644 --- a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp +++ b/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp @@ -1,6 +1,8 @@ #ifndef GOBJ_P_HPP #define GOBJ_P_HPP +#include <QObject> + namespace subGlobal { class GObjPrivate : public QObject diff --git a/Tests/QtAutogen/Parallel/CMakeLists.txt b/Tests/QtAutogen/Parallel/CMakeLists.txt new file mode 100644 index 0000000..9c64804 --- /dev/null +++ b/Tests/QtAutogen/Parallel/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel ${PARALLEL_SRC}) +set_target_properties(parallel PROPERTIES AUTOGEN_PARALLEL "") +target_link_libraries(parallel ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc b/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc new file mode 100644 index 0000000..0ea3537 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="aaa/bbb"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp b/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp new file mode 100644 index 0000000..850206f --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp @@ -0,0 +1,22 @@ +#include "item.hpp" + +namespace aaa { +namespace bbb { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + MocLocal obj; +} +} +} + +#include "aaa/bbb/item.moc" diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp b/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp new file mode 100644 index 0000000..0855043 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp @@ -0,0 +1,18 @@ +#ifndef AAA_BBB_ITEM_HPP +#define AAA_BBB_ITEM_HPP + +#include <QObject> + +namespace aaa { +namespace bbb { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} +} + +#endif diff --git a/Tests/QtAutogen/Parallel/aaa/data.qrc b/Tests/QtAutogen/Parallel/aaa/data.qrc new file mode 100644 index 0000000..379af60 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="aaa/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/aaa/item.cpp b/Tests/QtAutogen/Parallel/aaa/item.cpp new file mode 100644 index 0000000..e35d3d1 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/item.cpp @@ -0,0 +1,22 @@ +#include "item.hpp" +// Include ui_view.h only in header + +namespace aaa { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_ViewAAA ui; + MocLocal obj; +} +} + +#include "aaa/item.moc" diff --git a/Tests/QtAutogen/Parallel/aaa/item.hpp b/Tests/QtAutogen/Parallel/aaa/item.hpp new file mode 100644 index 0000000..875f72f --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/item.hpp @@ -0,0 +1,18 @@ +#ifndef AAA_ITEM_HPP +#define AAA_ITEM_HPP + +#include <QObject> +// Include ui_view.h only in header +#include <aaa/ui_view.h> + +namespace aaa { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/Parallel/aaa/view.ui b/Tests/QtAutogen/Parallel/aaa/view.ui new file mode 100644 index 0000000..0f09980 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewAAA</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/Parallel/bbb/aaa/data.qrc b/Tests/QtAutogen/Parallel/bbb/aaa/data.qrc new file mode 100644 index 0000000..da98009 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/aaa/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="bbb/aaa/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp b/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp new file mode 100644 index 0000000..7ad01c3 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp @@ -0,0 +1,22 @@ +#include "item.hpp" + +namespace bbb { +namespace aaa { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + MocLocal obj; +} +} +} + +#include "bbb/aaa/item.moc" diff --git a/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp b/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp new file mode 100644 index 0000000..be07ca8 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp @@ -0,0 +1,18 @@ +#ifndef BBB_AAA_ITEM_HPP +#define BBB_AAA_ITEM_HPP + +#include <QObject> + +namespace bbb { +namespace aaa { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} +} + +#endif diff --git a/Tests/QtAutogen/Parallel/bbb/data.qrc b/Tests/QtAutogen/Parallel/bbb/data.qrc new file mode 100644 index 0000000..5b080f5 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="bbb/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/bbb/item.cpp b/Tests/QtAutogen/Parallel/bbb/item.cpp new file mode 100644 index 0000000..9ef128e --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/item.cpp @@ -0,0 +1,23 @@ +#include "item.hpp" +// Include ui_view.h only in source +#include <bbb/ui_view.h> + +namespace bbb { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_ViewBBB ui; + MocLocal obj; +} +} + +#include "bbb/item.moc" diff --git a/Tests/QtAutogen/Parallel/bbb/item.hpp b/Tests/QtAutogen/Parallel/bbb/item.hpp new file mode 100644 index 0000000..d39a9d7 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/item.hpp @@ -0,0 +1,17 @@ +#ifndef BBB_ITEM_HPP +#define BBB_ITEM_HPP + +#include <QObject> +// Include ui_view.h only in source + +namespace bbb { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/Parallel/bbb/view.ui b/Tests/QtAutogen/Parallel/bbb/view.ui new file mode 100644 index 0000000..a8f506e --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewBBB</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/Parallel/ccc/data.qrc b/Tests/QtAutogen/Parallel/ccc/data.qrc new file mode 100644 index 0000000..f934c39 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="ccc/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/ccc/item.cpp b/Tests/QtAutogen/Parallel/ccc/item.cpp new file mode 100644 index 0000000..ab8a281 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/item.cpp @@ -0,0 +1,25 @@ +#include "item.hpp" +// Include ui_view.h in source and header +#include <ccc/ui_view.h> + +namespace ccc { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_ViewCCC ui; + MocLocal obj; +} +} + +// Include own moc files +#include "ccc/item.moc" +#include "moc_item.cpp" diff --git a/Tests/QtAutogen/Parallel/ccc/item.hpp b/Tests/QtAutogen/Parallel/ccc/item.hpp new file mode 100644 index 0000000..20d9dd9 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/item.hpp @@ -0,0 +1,18 @@ +#ifndef CCC_ITEM_HPP +#define CCC_ITEM_HPP + +#include <QObject> +// Include ui_view.h in source and header +#include <ccc/ui_view.h> + +namespace ccc { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/Parallel/ccc/view.ui b/Tests/QtAutogen/Parallel/ccc/view.ui new file mode 100644 index 0000000..7989c69 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewCCC</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/Parallel/data.qrc b/Tests/QtAutogen/Parallel/data.qrc new file mode 100644 index 0000000..4ce0b4e --- /dev/null +++ b/Tests/QtAutogen/Parallel/data.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>main.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/item.cpp b/Tests/QtAutogen/Parallel/item.cpp new file mode 100644 index 0000000..3d1fbe7 --- /dev/null +++ b/Tests/QtAutogen/Parallel/item.cpp @@ -0,0 +1,20 @@ +#include "item.hpp" +// Include ui_view.h in source and header +#include <ui_view.h> + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_View ui; + MocLocal obj; +} + +#include "item.moc" diff --git a/Tests/QtAutogen/Parallel/item.hpp b/Tests/QtAutogen/Parallel/item.hpp new file mode 100644 index 0000000..75e83f4 --- /dev/null +++ b/Tests/QtAutogen/Parallel/item.hpp @@ -0,0 +1,15 @@ +#ifndef ITEM_HPP +#define ITEM_HPP + +#include <QObject> +// Include ui_view.h in source and header +#include <ui_view.h> + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; + +#endif diff --git a/Tests/QtAutogen/Parallel/main.cpp b/Tests/QtAutogen/Parallel/main.cpp new file mode 100644 index 0000000..a4ffcb3 --- /dev/null +++ b/Tests/QtAutogen/Parallel/main.cpp @@ -0,0 +1,16 @@ +#include "aaa/bbb/item.hpp" +#include "aaa/item.hpp" +#include "bbb/aaa/item.hpp" +#include "bbb/item.hpp" +#include "ccc/item.hpp" + +int main(int argv, char** args) +{ + // Object instances + ::aaa::Item aaa_item; + ::aaa::bbb::Item aaa_bbb_item; + ::bbb::Item bbb_item; + ::bbb::aaa::Item bbb_aaa_item; + ::ccc::Item ccc_item; + return 0; +} diff --git a/Tests/QtAutogen/Parallel/parallel.cmake b/Tests/QtAutogen/Parallel/parallel.cmake new file mode 100644 index 0000000..551bcd8 --- /dev/null +++ b/Tests/QtAutogen/Parallel/parallel.cmake @@ -0,0 +1,24 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + + +set(PBASE ${CMAKE_CURRENT_LIST_DIR}) +set(PARALLEL_SRC + ${PBASE}/aaa/bbb/item.cpp + ${PBASE}/aaa/bbb/data.qrc + ${PBASE}/aaa/item.cpp + ${PBASE}/aaa/data.qrc + + ${PBASE}/bbb/aaa/item.cpp + ${PBASE}/bbb/aaa/data.qrc + ${PBASE}/bbb/item.cpp + ${PBASE}/bbb/data.qrc + + ${PBASE}/ccc/item.cpp + ${PBASE}/ccc/data.qrc + + ${PBASE}/item.cpp + ${PBASE}/data.qrc + ${PBASE}/main.cpp +) diff --git a/Tests/QtAutogen/Parallel/view.ui b/Tests/QtAutogen/Parallel/view.ui new file mode 100644 index 0000000..2ffe734 --- /dev/null +++ b/Tests/QtAutogen/Parallel/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>View</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/Parallel1/CMakeLists.txt b/Tests/QtAutogen/Parallel1/CMakeLists.txt new file mode 100644 index 0000000..9c0b4e5 --- /dev/null +++ b/Tests/QtAutogen/Parallel1/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel1) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel1 ${PARALLEL_SRC}) +set_target_properties(parallel1 PROPERTIES AUTOGEN_PARALLEL 1) +target_link_libraries(parallel1 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel2/CMakeLists.txt b/Tests/QtAutogen/Parallel2/CMakeLists.txt new file mode 100644 index 0000000..74c38f1 --- /dev/null +++ b/Tests/QtAutogen/Parallel2/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel2) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel2 ${PARALLEL_SRC}) +set_target_properties(parallel2 PROPERTIES AUTOGEN_PARALLEL 2) +target_link_libraries(parallel2 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel3/CMakeLists.txt b/Tests/QtAutogen/Parallel3/CMakeLists.txt new file mode 100644 index 0000000..c735531 --- /dev/null +++ b/Tests/QtAutogen/Parallel3/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel3) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel3 ${PARALLEL_SRC}) +set_target_properties(parallel3 PROPERTIES AUTOGEN_PARALLEL 3) +target_link_libraries(parallel3 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel4/CMakeLists.txt b/Tests/QtAutogen/Parallel4/CMakeLists.txt new file mode 100644 index 0000000..c012ccd --- /dev/null +++ b/Tests/QtAutogen/Parallel4/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel4) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel4 ${PARALLEL_SRC}) +set_target_properties(parallel4 PROPERTIES AUTOGEN_PARALLEL 4) +target_link_libraries(parallel4 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt new file mode 100644 index 0000000..3fd3ebc --- /dev/null +++ b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(ParallelAUTO) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallelAUTO ${PARALLEL_SRC}) +set_target_properties(parallelAUTO PROPERTIES AUTOGEN_PARALLEL "AUTO") +target_link_libraries(parallelAUTO ${QT_LIBRARIES}) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 8eb8568..e440b7f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -339,6 +339,7 @@ add_RunCMake_test(CPackInstallProperties) add_RunCMake_test(ExternalProject) add_RunCMake_test(FetchContent) add_RunCMake_test(CTestCommandLine) +add_RunCMake_test(CacheNewline) # Only run this test on unix platforms that support # symbolic links if(UNIX) @@ -351,8 +352,6 @@ add_RunCMake_test(IfacePaths_INCLUDE_DIRECTORIES TEST_DIR IfacePaths) set(IfacePaths_SOURCES_ARGS -DTEST_PROP=SOURCES) add_RunCMake_test(IfacePaths_SOURCES TEST_DIR IfacePaths) -add_RunCMake_test(COMPILE_LANGUAGE-genex) - # Matlab module related tests if(CMake_TEST_FindMatlab) add_RunCMake_test(FindMatlab) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-result.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-VS.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-VS.txt deleted file mode 100644 index 42c1485..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-VS.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at CompileDefinitions.cmake:5 \(target_compile_definitions\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Visual Studio generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-Xcode.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-Xcode.txt deleted file mode 100644 index 7879a79..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-Xcode.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at CompileDefinitions.cmake:5 \(target_compile_definitions\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Xcode generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions.cmake deleted file mode 100644 index 7935d88..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -enable_language(CXX) - -add_executable(main main.cpp) -target_compile_definitions(main PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-DANYTHING>) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-result.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-VS.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-VS.txt deleted file mode 100644 index 3806ed1..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-VS.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at IncludeDirectories.cmake:5 \(target_include_directories\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Visual Studio generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-Xcode.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-Xcode.txt deleted file mode 100644 index a3fb9c5..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-Xcode.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at IncludeDirectories.cmake:5 \(target_include_directories\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Xcode generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories.cmake deleted file mode 100644 index 31771f6..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -enable_language(CXX) - -add_executable(main main.cpp) -target_include_directories(main PRIVATE $<$<COMPILE_LANGUAGE:CXX>:anydir>) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-result.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-VS.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-VS.txt deleted file mode 100644 index 5d19153..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-VS.txt +++ /dev/null @@ -1,7 +0,0 @@ -CMake Error: - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Visual Studio generator. diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-Xcode.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-Xcode.txt deleted file mode 100644 index 4a4564e..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-Xcode.txt +++ /dev/null @@ -1,7 +0,0 @@ -CMake Error: - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Xcode generator. diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions.cmake deleted file mode 100644 index 3a07d7d..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -enable_language(CXX) - -add_executable(main main.cpp) -set_property(SOURCE main.cpp PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:CXX>:ANYTHING>) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/RunCMakeTest.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/RunCMakeTest.cmake deleted file mode 100644 index 1a93dcd..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/RunCMakeTest.cmake +++ /dev/null @@ -1,23 +0,0 @@ -include(RunCMake) - -if (RunCMake_GENERATOR STREQUAL "Xcode") - set(RunCMake-stderr-file CompileDefinitions-stderr-Xcode.txt) - run_cmake(CompileDefinitions) -elseif (RunCMake_GENERATOR MATCHES "Visual Studio") - set(RunCMake-stderr-file CompileDefinitions-stderr-VS.txt) - run_cmake(CompileDefinitions) -endif() -if (RunCMake_GENERATOR STREQUAL "Xcode") - set(RunCMake-stderr-file IncludeDirectories-stderr-Xcode.txt) - run_cmake(IncludeDirectories) -elseif (RunCMake_GENERATOR MATCHES "Visual Studio") - set(RunCMake-stderr-file IncludeDirectories-stderr-VS.txt) - run_cmake(IncludeDirectories) -endif() -if (RunCMake_GENERATOR STREQUAL "Xcode") - set(RunCMake-stderr-file PerSourceCompileDefinitions-stderr-Xcode.txt) - run_cmake(PerSourceCompileDefinitions) -elseif (RunCMake_GENERATOR MATCHES "Visual Studio") - set(RunCMake-stderr-file PerSourceCompileDefinitions-stderr-VS.txt) - run_cmake(PerSourceCompileDefinitions) -endif() diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/main.cpp b/Tests/RunCMake/COMPILE_LANGUAGE-genex/main.cpp deleted file mode 100644 index 766b775..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/main.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -int main() -{ - return 0; -} diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CMakeLists.txt b/Tests/RunCMake/CacheNewline/CMakeLists.txt index ef2163c..93ee9df 100644 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CMakeLists.txt +++ b/Tests/RunCMake/CacheNewline/CMakeLists.txt @@ -1,3 +1,3 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CacheNewline/CacheNewline-check.cmake b/Tests/RunCMake/CacheNewline/CacheNewline-check.cmake new file mode 100644 index 0000000..6534f63 --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CacheNewline-check.cmake @@ -0,0 +1,16 @@ +set(CACHE_EXPECTED_FILE "${RunCMake_TEST_SOURCE_DIR}/cache-regex.txt") +set(CACHE_ACTUAL_FILE "${RunCMake_BINARY_DIR}/CacheNewline-build/CMakeCache.txt") + +file(READ ${CACHE_EXPECTED_FILE} CACHE_EXPECTED) +string(REGEX REPLACE "\r\n" "\n" CACHE_EXPECTED "${CACHE_EXPECTED}") +string(REGEX REPLACE "\n+$" "" CACHE_EXPECTED "${CACHE_EXPECTED}") +file(READ ${CACHE_ACTUAL_FILE} CACHE_ACTUAL) +string(REGEX REPLACE "\r\n" "\n" CACHE_ACTUAL "${CACHE_ACTUAL}") +string(REGEX REPLACE "\n+$" "" CACHE_ACTUAL "${CACHE_ACTUAL}") + +if(NOT "${CACHE_ACTUAL}" MATCHES "${CACHE_EXPECTED}") + set(RunCMake_TEST_FAILED "${CACHE_ACTUAL_FILE} does not match ${CACHE_EXPECTED_FILE}: + +CMakeCache.txt contents = [\n${CACHE_ACTUAL}\n] +Expected = [\n${CACHE_EXPECTED}\n]") +endif() diff --git a/Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt b/Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt new file mode 100644 index 0000000..726c65c --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CacheNewline-stderr.txt @@ -0,0 +1,2 @@ +CMake Warning: + Value of NEWLINE_VARIABLE contained a newline; truncating diff --git a/Tests/RunCMake/CacheNewline/CacheNewline.cmake b/Tests/RunCMake/CacheNewline/CacheNewline.cmake new file mode 100644 index 0000000..81851db --- /dev/null +++ b/Tests/RunCMake/CacheNewline/CacheNewline.cmake @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.5) + +project(CacheNewlineTest NONE) + +set(NEWLINE_VARIABLE "a\nb" CACHE STRING "Offending entry") diff --git a/Tests/RunCMake/CacheNewline/RunCMakeTest.cmake b/Tests/RunCMake/CacheNewline/RunCMakeTest.cmake new file mode 100644 index 0000000..5e3d2d4 --- /dev/null +++ b/Tests/RunCMake/CacheNewline/RunCMakeTest.cmake @@ -0,0 +1,3 @@ +include(RunCMake) + +run_cmake(CacheNewline) diff --git a/Tests/RunCMake/CacheNewline/cache-regex.txt b/Tests/RunCMake/CacheNewline/cache-regex.txt new file mode 100644 index 0000000..b239dbc --- /dev/null +++ b/Tests/RunCMake/CacheNewline/cache-regex.txt @@ -0,0 +1,6 @@ +//Offending entry +NEWLINE_VARIABLE:STRING=a +# WARNING: Value of NEWLINE_VARIABLE contained a newline and was +# truncated\. Original value: +# a +# \\nb diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake index cfca8fe..cf81f36 100644 --- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake +++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake @@ -53,7 +53,16 @@ endif() add_subdirectory(lib_shared_and_static) -add_compiler_export_flags() +if(CMAKE_SYSTEM_NAME MATCHES "AIX" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + AND CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY) + # With GNU 7 on AIX, passing -fvisibility=hidden when driving the + # linker for a shared library drops the so init/destruct symbols. + # Just use the modern approach instead of testing the macro. + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +else() + add_compiler_export_flags() +endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt b/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt index c4a761c..9516a5a 100644 --- a/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt +++ b/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt @@ -1,7 +1,5 @@ include(GenerateExportHeader) -add_compiler_export_flags() - set(CMAKE_INCLUDE_CURRENT_DIR ON) add_library(libshared SHARED libshared.cpp) diff --git a/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt b/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt index 0fd136c..56e8335 100644 --- a/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt +++ b/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt @@ -2,8 +2,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include(GenerateExportHeader) -add_compiler_export_flags() - # Show that the export header has no effect on a static library. add_library(libstatic STATIC libstatic.cpp) diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp index 5408402..892cdcb 100644 --- a/Utilities/IWYU/mapping.imp +++ b/Utilities/IWYU/mapping.imp @@ -23,8 +23,11 @@ # HACK: check whether this can be removed with next iwyu release. { include: [ "<bits/shared_ptr.h>", private, "<memory>", public ] }, { include: [ "<bits/std_function.h>", private, "<functional>", public ] }, + { include: [ "<bits/stdint-intn.h>", private, "<stdint.h>", public ] }, + { include: [ "<bits/stdint-uintn.h>", private, "<stdint.h>", public ] }, { include: [ "<bits/time.h>", private, "<time.h>", public ] }, { include: [ "<bits/types/clock_t.h>", private, "<time.h>", public ] }, + { include: [ "<bits/types/mbstate_t.h>", private, "<wchar.h>", public ] }, { include: [ "<bits/types/struct_timespec.h>", private, "<time.h>", public ] }, { include: [ "<bits/types/struct_timeval.h>", private, "<time.h>", public ] }, { include: [ "<bits/types/struct_tm.h>", private, "<time.h>", public ] }, diff --git a/Utilities/Scripts/update-libuv.bash b/Utilities/Scripts/update-libuv.bash index d7a7d1f..6d423a7 100755 --- a/Utilities/Scripts/update-libuv.bash +++ b/Utilities/Scripts/update-libuv.bash @@ -20,6 +20,8 @@ extract_source () { git_archive pushd "${extractdir}/${name}-reduced" echo "* -whitespace" > .gitattributes + echo >> src/unix/aix-common.c + echo >> src/unix/ibmi.c popd } diff --git a/Utilities/cmlibuv/CMakeLists.txt b/Utilities/cmlibuv/CMakeLists.txt index 4c8e228..ba1638e 100644 --- a/Utilities/cmlibuv/CMakeLists.txt +++ b/Utilities/cmlibuv/CMakeLists.txt @@ -26,6 +26,7 @@ set(uv_sources src/threadpool.c src/uv-common.c src/uv-common.h + src/uv-data-getter-setters.c src/version.c ) if(WIN32) @@ -130,6 +131,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX") ) list(APPEND uv_sources src/unix/aix.c + src/unix/aix-common.c ) endif() @@ -170,7 +172,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") src/unix/fsevents.c src/unix/kqueue.c src/unix/proctitle.c - src/unix/pthread-barrier.c ) endif() diff --git a/Utilities/cmlibuv/LICENSE b/Utilities/cmlibuv/LICENSE index 41ba44c..28f1733 100644 --- a/Utilities/cmlibuv/LICENSE +++ b/Utilities/cmlibuv/LICENSE @@ -62,8 +62,8 @@ The externally maintained libraries used by libuv are: - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three clause BSD license. - - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile - Communications AB. Three clause BSD license. + - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB. + Three clause BSD license. - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement diff --git a/Utilities/cmlibuv/include/pthread-barrier.h b/Utilities/cmlibuv/include/pthread-barrier.h index 900ebed..07db9b8a 100644 --- a/Utilities/cmlibuv/include/pthread-barrier.h +++ b/Utilities/cmlibuv/include/pthread-barrier.h @@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #endif #define PTHREAD_BARRIER_SERIAL_THREAD 0x12345 +#define UV__PTHREAD_BARRIER_FALLBACK 1 /* * To maintain ABI compatibility with diff --git a/Utilities/cmlibuv/include/uv-errno.h b/Utilities/cmlibuv/include/uv-errno.h index f137151..8a41533 100644 --- a/Utilities/cmlibuv/include/uv-errno.h +++ b/Utilities/cmlibuv/include/uv-errno.h @@ -416,4 +416,16 @@ # define UV__EHOSTDOWN (-4031) #endif +#if defined(EREMOTEIO) && !defined(_WIN32) +# define UV__EREMOTEIO (-EREMOTEIO) +#else +# define UV__EREMOTEIO (-4030) +#endif + +#if defined(ENOTTY) && !defined(_WIN32) +# define UV__ENOTTY (-ENOTTY) +#else +# define UV__ENOTTY (-4029) +#endif + #endif /* UV_ERRNO_H_ */ diff --git a/Utilities/cmlibuv/include/uv-os390.h b/Utilities/cmlibuv/include/uv-os390.h index 58f9261..39e7384 100644 --- a/Utilities/cmlibuv/include/uv-os390.h +++ b/Utilities/cmlibuv/include/uv-os390.h @@ -27,4 +27,7 @@ #define UV_PLATFORM_LOOP_FIELDS \ void* ep; \ +#define UV_PLATFORM_FS_EVENT_FIELDS \ + char rfis_rftok[8]; \ + #endif /* UV_MVS_H */ diff --git a/Utilities/cmlibuv/include/uv-unix.h b/Utilities/cmlibuv/include/uv-unix.h index ff59bcb..d8d031f 100644 --- a/Utilities/cmlibuv/include/uv-unix.h +++ b/Utilities/cmlibuv/include/uv-unix.h @@ -50,6 +50,8 @@ # include "uv-linux.h" #elif defined (__MVS__) # include "uv-os390.h" +#elif defined(_PASE) +# include "uv-posix.h" #elif defined(_AIX) # include "uv-aix.h" #elif defined(__sun) @@ -125,6 +127,7 @@ typedef struct uv_buf_t { typedef int uv_file; typedef int uv_os_sock_t; typedef int uv_os_fd_t; +typedef pid_t uv_pid_t; #ifdef CMAKE_BOOTSTRAP #define UV_ONCE_INIT 0 @@ -379,4 +382,97 @@ typedef struct { uv_fs_event_cb cb; \ UV_PLATFORM_FS_EVENT_FIELDS \ +/* fs open() flags supported on this platform: */ +#if defined(O_APPEND) +# define UV_FS_O_APPEND O_APPEND +#else +# define UV_FS_O_APPEND 0 +#endif +#if defined(O_CREAT) +# define UV_FS_O_CREAT O_CREAT +#else +# define UV_FS_O_CREAT 0 +#endif +#if defined(O_DIRECT) +# define UV_FS_O_DIRECT O_DIRECT +#else +# define UV_FS_O_DIRECT 0 +#endif +#if defined(O_DIRECTORY) +# define UV_FS_O_DIRECTORY O_DIRECTORY +#else +# define UV_FS_O_DIRECTORY 0 +#endif +#if defined(O_DSYNC) +# define UV_FS_O_DSYNC O_DSYNC +#else +# define UV_FS_O_DSYNC 0 +#endif +#if defined(O_EXCL) +# define UV_FS_O_EXCL O_EXCL +#else +# define UV_FS_O_EXCL 0 +#endif +#if defined(O_EXLOCK) +# define UV_FS_O_EXLOCK O_EXLOCK +#else +# define UV_FS_O_EXLOCK 0 +#endif +#if defined(O_NOATIME) +# define UV_FS_O_NOATIME O_NOATIME +#else +# define UV_FS_O_NOATIME 0 +#endif +#if defined(O_NOCTTY) +# define UV_FS_O_NOCTTY O_NOCTTY +#else +# define UV_FS_O_NOCTTY 0 +#endif +#if defined(O_NOFOLLOW) +# define UV_FS_O_NOFOLLOW O_NOFOLLOW +#else +# define UV_FS_O_NOFOLLOW 0 +#endif +#if defined(O_NONBLOCK) +# define UV_FS_O_NONBLOCK O_NONBLOCK +#else +# define UV_FS_O_NONBLOCK 0 +#endif +#if defined(O_RDONLY) +# define UV_FS_O_RDONLY O_RDONLY +#else +# define UV_FS_O_RDONLY 0 +#endif +#if defined(O_RDWR) +# define UV_FS_O_RDWR O_RDWR +#else +# define UV_FS_O_RDWR 0 +#endif +#if defined(O_SYMLINK) +# define UV_FS_O_SYMLINK O_SYMLINK +#else +# define UV_FS_O_SYMLINK 0 +#endif +#if defined(O_SYNC) +# define UV_FS_O_SYNC O_SYNC +#else +# define UV_FS_O_SYNC 0 +#endif +#if defined(O_TRUNC) +# define UV_FS_O_TRUNC O_TRUNC +#else +# define UV_FS_O_TRUNC 0 +#endif +#if defined(O_WRONLY) +# define UV_FS_O_WRONLY O_WRONLY +#else +# define UV_FS_O_WRONLY 0 +#endif + +/* fs open() flags supported on other platforms: */ +#define UV_FS_O_RANDOM 0 +#define UV_FS_O_SHORT_LIVED 0 +#define UV_FS_O_SEQUENTIAL 0 +#define UV_FS_O_TEMPORARY 0 + #endif /* UV_UNIX_H */ diff --git a/Utilities/cmlibuv/include/uv-version.h b/Utilities/cmlibuv/include/uv-version.h index e165809..1dc63e5 100644 --- a/Utilities/cmlibuv/include/uv-version.h +++ b/Utilities/cmlibuv/include/uv-version.h @@ -31,7 +31,7 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 11 +#define UV_VERSION_MINOR 19 #define UV_VERSION_PATCH 1 #define UV_VERSION_IS_RELEASE 0 #define UV_VERSION_SUFFIX "dev" diff --git a/Utilities/cmlibuv/include/uv-win.h b/Utilities/cmlibuv/include/uv-win.h index d3e32a5..6486aab 100644 --- a/Utilities/cmlibuv/include/uv-win.h +++ b/Utilities/cmlibuv/include/uv-win.h @@ -20,7 +20,7 @@ */ #ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0502 +# define _WIN32_WINNT 0x0600 #endif #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) @@ -233,6 +233,7 @@ typedef struct uv_buf_t { typedef int uv_file; typedef SOCKET uv_os_sock_t; typedef HANDLE uv_os_fd_t; +typedef int uv_pid_t; typedef HANDLE uv_thread_t; @@ -659,3 +660,28 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); #ifndef X_OK #define X_OK 1 #endif + +/* fs open() flags supported on this platform: */ +#define UV_FS_O_APPEND _O_APPEND +#define UV_FS_O_CREAT _O_CREAT +#define UV_FS_O_EXCL _O_EXCL +#define UV_FS_O_RANDOM _O_RANDOM +#define UV_FS_O_RDONLY _O_RDONLY +#define UV_FS_O_RDWR _O_RDWR +#define UV_FS_O_SEQUENTIAL _O_SEQUENTIAL +#define UV_FS_O_SHORT_LIVED _O_SHORT_LIVED +#define UV_FS_O_TEMPORARY _O_TEMPORARY +#define UV_FS_O_TRUNC _O_TRUNC +#define UV_FS_O_WRONLY _O_WRONLY + +/* fs open() flags supported on other platforms (or mapped on this platform): */ +#define UV_FS_O_DIRECT 0x02000000 /* FILE_FLAG_NO_BUFFERING */ +#define UV_FS_O_DIRECTORY 0 +#define UV_FS_O_DSYNC 0x04000000 /* FILE_FLAG_WRITE_THROUGH */ +#define UV_FS_O_EXLOCK 0x10000000 /* EXCLUSIVE SHARING MODE */ +#define UV_FS_O_NOATIME 0 +#define UV_FS_O_NOCTTY 0 +#define UV_FS_O_NOFOLLOW 0 +#define UV_FS_O_NONBLOCK 0 +#define UV_FS_O_SYMLINK 0 +#define UV_FS_O_SYNC 0x08000000 /* FILE_FLAG_WRITE_THROUGH */ diff --git a/Utilities/cmlibuv/include/uv.h b/Utilities/cmlibuv/include/uv.h index 38f5676..328ce9e 100644 --- a/Utilities/cmlibuv/include/uv.h +++ b/Utilities/cmlibuv/include/uv.h @@ -144,6 +144,8 @@ extern "C" { XX(ENXIO, "no such device or address") \ XX(EMLINK, "too many links") \ XX(EHOSTDOWN, "host is down") \ + XX(EREMOTEIO, "remote I/O error") \ + XX(ENOTTY, "inappropriate ioctl for device") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ @@ -427,7 +429,17 @@ struct uv_handle_s { }; UV_EXTERN size_t uv_handle_size(uv_handle_type type); +UV_EXTERN uv_handle_type uv_handle_get_type(const uv_handle_t* handle); +UV_EXTERN const char* uv_handle_type_name(uv_handle_type type); +UV_EXTERN void* uv_handle_get_data(const uv_handle_t* handle); +UV_EXTERN uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle); +UV_EXTERN void uv_handle_set_data(uv_handle_t* handle, void* data); + UV_EXTERN size_t uv_req_size(uv_req_type type); +UV_EXTERN void* uv_req_get_data(const uv_req_t* req); +UV_EXTERN void uv_req_set_data(uv_req_t* req, void* data); +UV_EXTERN uv_req_type uv_req_get_type(const uv_req_t* req); +UV_EXTERN const char* uv_req_type_name(uv_req_type type); UV_EXTERN int uv_is_active(const uv_handle_t* handle); @@ -467,6 +479,8 @@ struct uv_stream_s { UV_STREAM_FIELDS }; +UV_EXTERN size_t uv_stream_get_write_queue_size(const uv_stream_t* stream); + UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client); @@ -644,6 +658,8 @@ UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb); UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle); +UV_EXTERN size_t uv_udp_get_send_queue_size(const uv_udp_t* handle); +UV_EXTERN size_t uv_udp_get_send_queue_count(const uv_udp_t* handle); /* @@ -712,6 +728,7 @@ UV_EXTERN int uv_pipe_getpeername(const uv_pipe_t* handle, UV_EXTERN void uv_pipe_pending_instances(uv_pipe_t* handle, int count); UV_EXTERN int uv_pipe_pending_count(uv_pipe_t* handle); UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); +UV_EXTERN int uv_pipe_chmod(uv_pipe_t* handle, int flags); struct uv_poll_s { @@ -723,7 +740,8 @@ struct uv_poll_s { enum uv_poll_event { UV_READABLE = 1, UV_WRITABLE = 2, - UV_DISCONNECT = 4 + UV_DISCONNECT = 4, + UV_PRIORITIZED = 8 }; UV_EXTERN int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd); @@ -962,6 +980,7 @@ UV_EXTERN int uv_spawn(uv_loop_t* loop, const uv_process_options_t* options); UV_EXTERN int uv_process_kill(uv_process_t*, int signum); UV_EXTERN int uv_kill(int pid, int signum); +UV_EXTERN uv_pid_t uv_process_get_pid(const uv_process_t*); /* @@ -1038,6 +1057,7 @@ UV_EXTERN int uv_get_process_title(char* buffer, size_t size); UV_EXTERN int uv_set_process_title(const char* title); UV_EXTERN int uv_resident_set_memory(size_t* rss); UV_EXTERN int uv_uptime(double* uptime); +UV_EXTERN uv_os_fd_t uv_get_osfhandle(int fd); typedef struct { long tv_sec; @@ -1069,6 +1089,8 @@ UV_EXTERN int uv_os_homedir(char* buffer, size_t* size); UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size); UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd); UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); +UV_EXTERN uv_pid_t uv_os_getpid(void); +UV_EXTERN uv_pid_t uv_os_getppid(void); UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); @@ -1115,7 +1137,8 @@ typedef enum { UV_FS_READLINK, UV_FS_CHOWN, UV_FS_FCHOWN, - UV_FS_REALPATH + UV_FS_REALPATH, + UV_FS_COPYFILE } uv_fs_type; /* uv_fs_t is a subclass of uv_req_t. */ @@ -1131,6 +1154,12 @@ struct uv_fs_s { UV_FS_PRIVATE_FIELDS }; +UV_EXTERN uv_fs_type uv_fs_get_type(const uv_fs_t*); +UV_EXTERN ssize_t uv_fs_get_result(const uv_fs_t*); +UV_EXTERN void* uv_fs_get_ptr(const uv_fs_t*); +UV_EXTERN const char* uv_fs_get_path(const uv_fs_t*); +UV_EXTERN uv_stat_t* uv_fs_get_statbuf(uv_fs_t*); + UV_EXTERN void uv_fs_req_cleanup(uv_fs_t* req); UV_EXTERN int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, @@ -1160,6 +1189,18 @@ UV_EXTERN int uv_fs_write(uv_loop_t* loop, unsigned int nbufs, int64_t offset, uv_fs_cb cb); +/* + * This flag can be used with uv_fs_copyfile() to return an error if the + * destination already exists. + */ +#define UV_FS_COPYFILE_EXCL 0x0001 + +UV_EXTERN int uv_fs_copyfile(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + int flags, + uv_fs_cb cb); UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1393,6 +1434,21 @@ UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size); UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst); +#if defined(IF_NAMESIZE) +# define UV_IF_NAMESIZE (IF_NAMESIZE + 1) +#elif defined(IFNAMSIZ) +# define UV_IF_NAMESIZE (IFNAMSIZ + 1) +#else +# define UV_IF_NAMESIZE (16 + 1) +#endif + +UV_EXTERN int uv_if_indextoname(unsigned int ifindex, + char* buffer, + size_t* size); +UV_EXTERN int uv_if_indextoiid(unsigned int ifindex, + char* buffer, + size_t* size); + UV_EXTERN int uv_exepath(char* buffer, size_t* size); UV_EXTERN int uv_cwd(char* buffer, size_t* size); @@ -1412,6 +1468,7 @@ UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr); UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib); UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); +UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle); UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); @@ -1484,6 +1541,8 @@ struct uv_loop_s { UV_LOOP_PRIVATE_FIELDS }; +UV_EXTERN void* uv_loop_get_data(const uv_loop_t*); +UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data); /* Don't export the private CPP symbols. */ #undef UV_HANDLE_TYPE_PRIVATE diff --git a/Utilities/cmlibuv/src/threadpool.c b/Utilities/cmlibuv/src/threadpool.c index 1089341..413d1c2 100644 --- a/Utilities/cmlibuv/src/threadpool.c +++ b/Utilities/cmlibuv/src/threadpool.c @@ -38,7 +38,6 @@ static uv_thread_t* threads; static uv_thread_t default_threads[4]; static QUEUE exit_message; static QUEUE wq; -static volatile int initialized; static void uv__cancelled(struct uv__work* w) { @@ -53,7 +52,8 @@ static void worker(void* arg) { struct uv__work* w; QUEUE* q; - (void) arg; + uv_sem_post((uv_sem_t*) arg); + arg = NULL; for (;;) { uv_mutex_lock(&mutex); @@ -105,7 +105,7 @@ static void post(QUEUE* q) { UV_DESTRUCTOR(static void cleanup(void)) { unsigned int i; - if (initialized == 0) + if (nthreads == 0) return; post(&exit_message); @@ -122,7 +122,6 @@ UV_DESTRUCTOR(static void cleanup(void)) { threads = NULL; nthreads = 0; - initialized = 0; } #endif @@ -130,6 +129,7 @@ UV_DESTRUCTOR(static void cleanup(void)) { static void init_threads(void) { unsigned int i; const char* val; + uv_sem_t sem; nthreads = ARRAY_SIZE(default_threads); val = getenv("UV_THREADPOOL_SIZE"); @@ -157,11 +157,17 @@ static void init_threads(void) { QUEUE_INIT(&wq); + if (uv_sem_init(&sem, 0)) + abort(); + for (i = 0; i < nthreads; i++) - if (uv_thread_create(threads + i, worker, NULL)) + if (uv_thread_create(threads + i, worker, &sem)) abort(); - initialized = 1; + for (i = 0; i < nthreads; i++) + uv_sem_wait(&sem); + + uv_sem_destroy(&sem); } diff --git a/Utilities/cmlibuv/src/unix/aix-common.c b/Utilities/cmlibuv/src/unix/aix-common.c new file mode 100644 index 0000000..c2217fb --- /dev/null +++ b/Utilities/cmlibuv/src/unix/aix-common.c @@ -0,0 +1,292 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <sys/time.h> +#include <unistd.h> +#include <fcntl.h> +#include <utmp.h> +#include <libgen.h> + +#include <sys/protosw.h> +#include <procinfo.h> +#include <sys/proc.h> +#include <sys/procfs.h> + +#include <sys/poll.h> + +#include <sys/pollset.h> +#include <ctype.h> + +#include <sys/mntctl.h> +#include <sys/vmount.h> +#include <limits.h> +#include <strings.h> +#include <sys/vnode.h> + +uint64_t uv__hrtime(uv_clocktype_t type) { + uint64_t G = 1000000000; + timebasestruct_t t; + read_wall_time(&t, TIMEBASE_SZ); + time_base_to_time(&t, TIMEBASE_SZ); + return (uint64_t) t.tb_high * G + t.tb_low; +} + + +/* + * We could use a static buffer for the path manipulations that we need outside + * of the function, but this function could be called by multiple consumers and + * we don't want to potentially create a race condition in the use of snprintf. + * There is no direct way of getting the exe path in AIX - either through /procfs + * or through some libc APIs. The below approach is to parse the argv[0]'s pattern + * and use it in conjunction with PATH environment variable to craft one. + */ +int uv_exepath(char* buffer, size_t* size) { + int res; + char args[PATH_MAX]; + char abspath[PATH_MAX]; + size_t abspath_size; + struct procsinfo pi; + + if (buffer == NULL || size == NULL || *size == 0) + return -EINVAL; + + pi.pi_pid = getpid(); + res = getargs(&pi, sizeof(pi), args, sizeof(args)); + if (res < 0) + return -EINVAL; + + /* + * Possibilities for args: + * i) an absolute path such as: /home/user/myprojects/nodejs/node + * ii) a relative path such as: ./node or ../myprojects/nodejs/node + * iii) a bare filename such as "node", after exporting PATH variable + * to its location. + */ + + /* Case i) and ii) absolute or relative paths */ + if (strchr(args, '/') != NULL) { + if (realpath(args, abspath) != abspath) + return -errno; + + abspath_size = strlen(abspath); + + *size -= 1; + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + return 0; + } else { + /* Case iii). Search PATH environment variable */ + char trypath[PATH_MAX]; + char *clonedpath = NULL; + char *token = NULL; + char *path = getenv("PATH"); + + if (path == NULL) + return -EINVAL; + + clonedpath = uv__strdup(path); + if (clonedpath == NULL) + return -ENOMEM; + + token = strtok(clonedpath, ":"); + while (token != NULL) { + snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); + if (realpath(trypath, abspath) == abspath) { + /* Check the match is executable */ + if (access(abspath, X_OK) == 0) { + abspath_size = strlen(abspath); + + *size -= 1; + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + uv__free(clonedpath); + return 0; + } + } + token = strtok(NULL, ":"); + } + uv__free(clonedpath); + + /* Out of tokens (path entries), and no match found */ + return -EINVAL; + } +} + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; ++i) { + uv__free(cpu_infos[i].model); + } + + uv__free(cpu_infos); +} + + +int uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + uv_interface_address_t* address; + int sockfd, inet6, size = 1; + struct ifconf ifc; + struct ifreq *ifr, *p, flg; + struct sockaddr_dl* sa_addr; + + *count = 0; + + if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { + return -errno; + } + + if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) { + uv__close(sockfd); + return -errno; + } + + ifc.ifc_req = (struct ifreq*)uv__malloc(size); + ifc.ifc_len = size; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { + uv__close(sockfd); + return -errno; + } + +#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) + + /* Count all up and running ipv4/ipv6 addresses */ + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv__close(sockfd); + return -errno; + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + (*count)++; + } + + /* Alloc the return interface structs */ + *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + uv__close(sockfd); + return -ENOMEM; + } + address = *addresses; + + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + inet6 = (p->ifr_addr.sa_family == AF_INET6); + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv__close(sockfd); + return -ENOSYS; + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + /* All conditions above must match count loop */ + + address->name = uv__strdup(p->ifr_name); + + if (inet6) + address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); + else + address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); + + sa_addr = (struct sockaddr_dl*) &p->ifr_addr; + memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); + + if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) { + uv__close(sockfd); + return -ENOSYS; + } + + if (inet6) + address->netmask.netmask6 = *((struct sockaddr_in6*) &p->ifr_addr); + else + address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr); + + address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; + + address++; + } + +#undef ADDR_SIZE + + uv__close(sockfd); + return 0; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; ++i) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} diff --git a/Utilities/cmlibuv/src/unix/aix.c b/Utilities/cmlibuv/src/unix/aix.c index 388c9cc..fd41309 100644 --- a/Utilities/cmlibuv/src/unix/aix.c +++ b/Utilities/cmlibuv/src/unix/aix.c @@ -1,4 +1,5 @@ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the @@ -64,11 +65,18 @@ #define RDWR_BUF_SIZE 4096 #define EQ(a,b) (strcmp(a,b) == 0) +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; static void* args_mem = NULL; static char** process_argv = NULL; static int process_argc = 0; static char* process_title_ptr = NULL; +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + int uv__platform_loop_init(uv_loop_t* loop) { loop->fs_fd = -1; @@ -316,104 +324,6 @@ update_timeout: } -uint64_t uv__hrtime(uv_clocktype_t type) { - uint64_t G = 1000000000; - timebasestruct_t t; - read_wall_time(&t, TIMEBASE_SZ); - time_base_to_time(&t, TIMEBASE_SZ); - return (uint64_t) t.tb_high * G + t.tb_low; -} - - -/* - * We could use a static buffer for the path manipulations that we need outside - * of the function, but this function could be called by multiple consumers and - * we don't want to potentially create a race condition in the use of snprintf. - * There is no direct way of getting the exe path in AIX - either through /procfs - * or through some libc APIs. The below approach is to parse the argv[0]'s pattern - * and use it in conjunction with PATH environment variable to craft one. - */ -int uv_exepath(char* buffer, size_t* size) { - int res; - char args[PATH_MAX]; - char abspath[PATH_MAX]; - size_t abspath_size; - struct procsinfo pi; - - if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; - - pi.pi_pid = getpid(); - res = getargs(&pi, sizeof(pi), args, sizeof(args)); - if (res < 0) - return -EINVAL; - - /* - * Possibilities for args: - * i) an absolute path such as: /home/user/myprojects/nodejs/node - * ii) a relative path such as: ./node or ../myprojects/nodejs/node - * iii) a bare filename such as "node", after exporting PATH variable - * to its location. - */ - - /* Case i) and ii) absolute or relative paths */ - if (strchr(args, '/') != NULL) { - if (realpath(args, abspath) != abspath) - return -errno; - - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - return 0; - } else { - /* Case iii). Search PATH environment variable */ - char trypath[PATH_MAX]; - char *clonedpath = NULL; - char *token = NULL; - char *path = getenv("PATH"); - - if (path == NULL) - return -EINVAL; - - clonedpath = uv__strdup(path); - if (clonedpath == NULL) - return -ENOMEM; - - token = strtok(clonedpath, ":"); - while (token != NULL) { - snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); - if (realpath(trypath, abspath) == abspath) { - /* Check the match is executable */ - if (access(abspath, X_OK) == 0) { - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - uv__free(clonedpath); - return 0; - } - } - token = strtok(NULL, ":"); - } - uv__free(clonedpath); - - /* Out of tokens (path entries), and no match found */ - return -EINVAL; - } -} - - uint64_t uv_get_free_memory(void) { perfstat_memory_total_t mem_total; int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1); @@ -855,6 +765,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv__io_init(&handle->event_watcher, uv__ahafs_event, fd); handle->path = uv__strdup(filename); handle->cb = cb; + handle->dir_filename = NULL; uv__io_start(handle->loop, &handle->event_watcher, POLLIN); @@ -952,6 +863,9 @@ int uv_set_process_title(const char* title) { if (new_title == NULL) return -ENOMEM; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + /* If this is the first time this is set, * don't free and set argv[1] to NULL. */ @@ -964,6 +878,8 @@ int uv_set_process_title(const char* title) { if (process_argc > 1) process_argv[1] = NULL; + uv_mutex_unlock(&process_title_mutex); + return 0; } @@ -976,8 +892,13 @@ int uv_get_process_title(char* buffer, size_t size) { else if (size <= len) return -ENOBUFS; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + memcpy(buffer, process_argv[0], len + 1); + uv_mutex_unlock(&process_title_mutex); + return 0; } @@ -1017,6 +938,7 @@ int uv_uptime(double* uptime) { size_t entries = 0; time_t boot_time; + boot_time = 0; utmpname(UTMP_FILE); setutent(); @@ -1093,130 +1015,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; ++i) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); -} - - -int uv_interface_addresses(uv_interface_address_t** addresses, - int* count) { - uv_interface_address_t* address; - int sockfd, size = 1; - struct ifconf ifc; - struct ifreq *ifr, *p, flg; - - *count = 0; - - if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { - return -errno; - } - - if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) { - uv__close(sockfd); - return -errno; - } - - ifc.ifc_req = (struct ifreq*)uv__malloc(size); - ifc.ifc_len = size; - if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { - uv__close(sockfd); - return -errno; - } - -#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) - - /* Count all up and running ipv4/ipv6 addresses */ - ifr = ifc.ifc_req; - while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { - p = ifr; - ifr = (struct ifreq*) - ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); - - if (!(p->ifr_addr.sa_family == AF_INET6 || - p->ifr_addr.sa_family == AF_INET)) - continue; - - memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); - if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { - uv__close(sockfd); - return -errno; - } - - if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) - continue; - - (*count)++; - } - - /* Alloc the return interface structs */ - *addresses = (uv_interface_address_t*) - uv__malloc(*count * sizeof(uv_interface_address_t)); - if (!(*addresses)) { - uv__close(sockfd); - return -ENOMEM; - } - address = *addresses; - - ifr = ifc.ifc_req; - while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { - p = ifr; - ifr = (struct ifreq*) - ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); - - if (!(p->ifr_addr.sa_family == AF_INET6 || - p->ifr_addr.sa_family == AF_INET)) - continue; - - memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); - if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { - uv__close(sockfd); - return -ENOSYS; - } - - if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) - continue; - - /* All conditions above must match count loop */ - - address->name = uv__strdup(p->ifr_name); - - if (p->ifr_addr.sa_family == AF_INET6) { - address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); - } else { - address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); - } - - /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ - - address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; - - address++; - } - -#undef ADDR_SIZE - - uv__close(sockfd); - return 0; -} - - -void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count) { - int i; - - for (i = 0; i < count; ++i) { - uv__free(addresses[i].name); - } - - uv__free(addresses); -} - void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct pollfd* events; uintptr_t i; diff --git a/Utilities/cmlibuv/src/unix/android-ifaddrs.c b/Utilities/cmlibuv/src/unix/android-ifaddrs.c index 30f681b..bf30b14 100644 --- a/Utilities/cmlibuv/src/unix/android-ifaddrs.c +++ b/Utilities/cmlibuv/src/unix/android-ifaddrs.c @@ -43,9 +43,10 @@ typedef struct NetlinkList unsigned int m_size; } NetlinkList; -static int netlink_socket(void) +static int netlink_socket(pid_t *p_pid) { struct sockaddr_nl l_addr; + socklen_t l_len; int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if(l_socket < 0) @@ -61,6 +62,14 @@ static int netlink_socket(void) return -1; } + l_len = sizeof(l_addr); + if(getsockname(l_socket, (struct sockaddr *)&l_addr, &l_len) < 0) + { + close(l_socket); + return -1; + } + *p_pid = l_addr.nl_pid; + return l_socket; } @@ -128,7 +137,7 @@ static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) } } -static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_done) +static struct nlmsghdr *getNetlinkResponse(int p_socket, pid_t p_pid, int *p_size, int *p_done) { size_t l_size = 4096; void *l_buffer = NULL; @@ -153,11 +162,10 @@ static struct nlmsghdr *getNetlinkResponse(int p_socket, int *p_size, int *p_don } if(l_read >= 0) { - pid_t l_pid = getpid(); struct nlmsghdr *l_hdr; for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read)) { - if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) + if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) { continue; } @@ -207,7 +215,7 @@ static void freeResultList(NetlinkList *p_list) } } -static NetlinkList *getResultList(int p_socket, int p_request) +static NetlinkList *getResultList(int p_socket, int p_request, pid_t p_pid) { int l_size; int l_done; @@ -227,7 +235,7 @@ static NetlinkList *getResultList(int p_socket, int p_request) { NetlinkList *l_item; - struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done); + struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, p_pid, &l_size, &l_done); /* Error */ if(!l_hdr) { @@ -449,7 +457,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, char *l_name; char *l_addr; - for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); if(l_info->ifa_family == AF_PACKET) @@ -471,7 +479,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); break; case IFA_LABEL: - l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); + l_nameSize += NLMSG_ALIGN(l_rtaDataSize + 1); break; default: break; @@ -496,7 +504,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, } l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); - for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { void *l_rtaData = RTA_DATA(l_rta); size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); @@ -559,7 +567,7 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, { unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen); - char l_mask[16] = {0}; + unsigned char l_mask[16] = {0}; unsigned i; for(i=0; i<(l_prefix/8); ++i) { @@ -578,18 +586,17 @@ static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, return 0; } -static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList) +static int interpretLinks(int p_socket, pid_t p_pid, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList) { int l_numLinks = 0; - pid_t l_pid = getpid(); for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) { unsigned int l_nlsize = p_netlinkList->m_size; struct nlmsghdr *l_hdr; for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { - if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) + if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) { continue; } @@ -612,16 +619,15 @@ static int interpretLinks(int p_socket, NetlinkList *p_netlinkList, struct ifadd return l_numLinks; } -static int interpretAddrs(int p_socket, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks) +static int interpretAddrs(int p_socket, pid_t p_pid, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks) { - pid_t l_pid = getpid(); for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) { unsigned int l_nlsize = p_netlinkList->m_size; struct nlmsghdr *l_hdr; for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { - if((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) + if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) { continue; } @@ -648,6 +654,7 @@ int getifaddrs(struct ifaddrs **ifap) int l_socket; int l_result; int l_numLinks; + pid_t l_pid; NetlinkList *l_linkResults; NetlinkList *l_addrResults; @@ -657,20 +664,20 @@ int getifaddrs(struct ifaddrs **ifap) } *ifap = NULL; - l_socket = netlink_socket(); + l_socket = netlink_socket(&l_pid); if(l_socket < 0) { return -1; } - l_linkResults = getResultList(l_socket, RTM_GETLINK); + l_linkResults = getResultList(l_socket, RTM_GETLINK, l_pid); if(!l_linkResults) { close(l_socket); return -1; } - l_addrResults = getResultList(l_socket, RTM_GETADDR); + l_addrResults = getResultList(l_socket, RTM_GETADDR, l_pid); if(!l_addrResults) { close(l_socket); @@ -679,8 +686,8 @@ int getifaddrs(struct ifaddrs **ifap) } l_result = 0; - l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); - if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) + l_numLinks = interpretLinks(l_socket, l_pid, l_linkResults, ifap); + if(l_numLinks == -1 || interpretAddrs(l_socket, l_pid, l_addrResults, ifap, l_numLinks) == -1) { l_result = -1; } diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h index 9dac255..7cac1f9 100644 --- a/Utilities/cmlibuv/src/unix/atomic-ops.h +++ b/Utilities/cmlibuv/src/unix/atomic-ops.h @@ -20,7 +20,6 @@ #if defined(__SUNPRO_C) || defined(__SUNPRO_CC) #include <atomic.h> -#define __sync_val_compare_and_swap(p, o, n) atomic_cas_ptr(p, o, n) #endif UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); @@ -49,6 +48,8 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { return oldval; else return op4; +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) + return atomic_cas_uint(ptr, oldval, newval); #else return __sync_val_compare_and_swap(ptr, oldval, newval); #endif @@ -83,6 +84,8 @@ UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) { return oldval; else return op4; +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) + return atomic_cas_ulong(ptr, oldval, newval); #else return __sync_val_compare_and_swap(ptr, oldval, newval); #endif diff --git a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c index 4147894..ea3166c 100644 --- a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c +++ b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c @@ -31,11 +31,20 @@ #include <net/if_dl.h> #endif -static int uv__ifaddr_exclude(struct ifaddrs *ent) { +static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) return 1; if (ent->ifa_addr == NULL) return 1; +#if !defined(__CYGWIN__) && !defined(__MSYS__) + /* + * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, just see whether `sa_family` + * equals to `AF_LINK` or not. Otherwise, the result depends on the operation + * system with `AF_LINK` or `PF_INET`. + */ + if (exclude_type == UV__EXCLUDE_IFPHYS) + return (ent->ifa_addr->sa_family != AF_LINK); +#endif #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) /* * On BSD getifaddrs returns information related to the raw underlying @@ -43,7 +52,11 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent) { */ if (ent->ifa_addr->sa_family == AF_LINK) return 1; -#elif defined(__NetBSD__) || defined(__OpenBSD__) +#elif defined(__NetBSD__) + if (ent->ifa_addr->sa_family != PF_INET && + ent->ifa_addr->sa_family != PF_INET6) + return 1; +#elif defined(__OpenBSD__) if (ent->ifa_addr->sa_family != PF_INET) return 1; #endif @@ -63,7 +76,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent)) + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) continue; (*count)++; } @@ -78,7 +91,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent)) + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) continue; address->name = uv__strdup(ent->ifa_name); @@ -102,7 +115,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent)) + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) continue; address = *addresses; diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c index 30cdaef..c7e431e 100644 --- a/Utilities/cmlibuv/src/unix/core.c +++ b/Utilities/cmlibuv/src/unix/core.c @@ -28,7 +28,6 @@ #include <errno.h> #include <assert.h> #include <unistd.h> -#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -59,13 +58,19 @@ #if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) + defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) # include <sys/sysctl.h> # include <sys/filio.h> # include <sys/wait.h> # define UV__O_CLOEXEC O_CLOEXEC # if defined(__FreeBSD__) && __FreeBSD__ >= 10 # define uv__accept4 accept4 +# endif +# if defined(__NetBSD__) +# define uv__accept4(a, b, c, d) paccept((a), (b), (c), NULL, (d)) +# endif +# if (defined(__FreeBSD__) && __FreeBSD__ >= 10) || defined(__NetBSD__) # define UV__SOCK_NONBLOCK SOCK_NONBLOCK # define UV__SOCK_CLOEXEC SOCK_CLOEXEC # endif @@ -82,6 +87,10 @@ #include <sys/ioctl.h> #endif +#if !defined(__MVS__) +#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */ +#endif + /* Fallback for the maximum hostname length */ #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 256 @@ -459,7 +468,9 @@ int uv__accept(int sockfd) { assert(sockfd >= 0); while (1) { -#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) +#if defined(__linux__) || \ + (defined(__FreeBSD__) && __FreeBSD__ >= 10) || \ + defined(__NetBSD__) static int no_accept4; if (no_accept4) @@ -835,7 +846,7 @@ void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) { void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { - assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP))); + assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); assert(0 != events); assert(w->fd >= 0); assert(w->fd < INT_MAX); @@ -863,7 +874,7 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { - assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP))); + assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); assert(0 != events); if (w->fd == -1) @@ -895,7 +906,7 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { void uv__io_close(uv_loop_t* loop, uv__io_t* w) { - uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP); + uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); QUEUE_REMOVE(&w->pending_queue); /* Remove stale events for this file descriptor */ @@ -910,7 +921,7 @@ void uv__io_feed(uv_loop_t* loop, uv__io_t* w) { int uv__io_active(const uv__io_t* w, unsigned int events) { - assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP))); + assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); assert(0 != events); return 0 != (w->pevents & events); } @@ -985,7 +996,7 @@ int uv__open_cloexec(const char* path, int flags) { int uv__dup2_cloexec(int oldfd, int newfd) { int r; -#if defined(__FreeBSD__) && __FreeBSD__ >= 10 +#if (defined(__FreeBSD__) && __FreeBSD__ >= 10) || defined(__NetBSD__) r = dup3(oldfd, newfd, O_CLOEXEC); if (r == -1) return -errno; @@ -1289,6 +1300,9 @@ int uv_os_setenv(const char* name, const char* value) { int uv_os_unsetenv(const char* name) { + if (name == NULL) + return -EINVAL; + if (unsetenv(name) != 0) return -errno; @@ -1324,3 +1338,18 @@ int uv_os_gethostname(char* buffer, size_t* size) { *size = len; return 0; } + + +uv_os_fd_t uv_get_osfhandle(int fd) { + return fd; +} + + +uv_pid_t uv_os_getpid(void) { + return getpid(); +} + + +uv_pid_t uv_os_getppid(void) { + return getppid(); +} diff --git a/Utilities/cmlibuv/src/unix/freebsd.c b/Utilities/cmlibuv/src/unix/freebsd.c index e52ae99..f2b3f24 100644 --- a/Utilities/cmlibuv/src/unix/freebsd.c +++ b/Utilities/cmlibuv/src/unix/freebsd.c @@ -25,7 +25,6 @@ #include <string.h> #include <errno.h> -#include <kvm.h> #include <paths.h> #include <sys/user.h> #include <sys/types.h> @@ -48,9 +47,16 @@ # define CP_INTR 4 #endif +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; static char *process_title; +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + int uv__platform_loop_init(uv_loop_t* loop) { return uv__kqueue_init(loop); } @@ -161,9 +167,20 @@ char** uv_setup_args(int argc, char** argv) { int uv_set_process_title(const char* title) { int oid[4]; + char* new_title; + + new_title = uv__strdup(title); + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (process_title == NULL) { + uv_mutex_unlock(&process_title_mutex); + return -ENOMEM; + } uv__free(process_title); - process_title = uv__strdup(title); + process_title = new_title; oid[0] = CTL_KERN; oid[1] = KERN_PROC; @@ -177,6 +194,8 @@ int uv_set_process_title(const char* title) { process_title, strlen(process_title) + 1); + uv_mutex_unlock(&process_title_mutex); + return 0; } @@ -187,51 +206,54 @@ int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return -EINVAL; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + if (process_title) { len = strlen(process_title) + 1; - if (size < len) + if (size < len) { + uv_mutex_unlock(&process_title_mutex); return -ENOBUFS; + } memcpy(buffer, process_title, len); } else { len = 0; } + uv_mutex_unlock(&process_title_mutex); + buffer[len] = '\0'; return 0; } - int uv_resident_set_memory(size_t* rss) { - kvm_t *kd = NULL; - struct kinfo_proc *kinfo = NULL; - pid_t pid; - int nprocs; - size_t page_size = getpagesize(); + struct kinfo_proc kinfo; + size_t page_size; + size_t kinfo_size; + int mib[4]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); - pid = getpid(); + kinfo_size = sizeof(kinfo); - kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); - if (kd == NULL) goto error; + if (sysctl(mib, 4, &kinfo, &kinfo_size, NULL, 0)) + return -errno; - kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs); - if (kinfo == NULL) goto error; + page_size = getpagesize(); #ifdef __DragonFly__ - *rss = kinfo->kp_vm_rssize * page_size; + *rss = kinfo.kp_vm_rssize * page_size; #else - *rss = kinfo->ki_rssize * page_size; + *rss = kinfo.ki_rssize * page_size; #endif - kvm_close(kd); - return 0; - -error: - if (kd) kvm_close(kd); - return -EPERM; } @@ -254,6 +276,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { uv_cpu_info_t* cpu_info; const char* maxcpus_key; const char* cptimes_key; + const char* model_key; char model[512]; long* cp_times; int numcpus; @@ -272,8 +295,20 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { cptimes_key = "kern.cp_times"; #endif +#if defined(__arm__) || defined(__aarch64__) + /* The key hw.model and hw.clockrate are not available on FreeBSD ARM. */ + model_key = "hw.machine"; + cpuspeed = 0; +#else + model_key = "hw.model"; + + size = sizeof(cpuspeed); + if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0)) + return -errno; +#endif + size = sizeof(model); - if (sysctlbyname("hw.model", &model, &size, NULL, 0)) + if (sysctlbyname(model_key, &model, &size, NULL, 0)) return -errno; size = sizeof(numcpus); @@ -286,12 +321,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { *count = numcpus; - size = sizeof(cpuspeed); - if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0)) { - uv__free(*cpu_infos); - return -errno; - } - /* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of * ncpu. */ diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c index 82c91ef..0a4c183 100644 --- a/Utilities/cmlibuv/src/unix/fs.c +++ b/Utilities/cmlibuv/src/unix/fs.c @@ -60,8 +60,14 @@ # include <sys/sendfile.h> #endif +#if defined(__APPLE__) +# include <copyfile.h> +#endif + #define INIT(subtype) \ do { \ + if (req == NULL) \ + return -EINVAL; \ req->type = UV_FS; \ if (cb != NULL) \ uv__req_init(loop, req, UV_FS); \ @@ -126,26 +132,33 @@ while (0) -static ssize_t uv__fs_fdatasync(uv_fs_t* req) { -#if defined(__linux__) || defined(__sun) || defined(__NetBSD__) - return fdatasync(req->file); -#elif defined(__APPLE__) +static ssize_t uv__fs_fsync(uv_fs_t* req) { +#if defined(__APPLE__) /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache * to the drive platters. This is in contrast to Linux's fdatasync and fsync * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent - * for flushing buffered data to permanent storage. + * for flushing buffered data to permanent storage. If F_FULLFSYNC is not + * supported by the file system we should fall back to fsync(). This is the + * same approach taken by sqlite. */ - return fcntl(req->file, F_FULLFSYNC); + int r; + + r = fcntl(req->file, F_FULLFSYNC); + if (r != 0 && errno == ENOTTY) + r = fsync(req->file); + return r; #else return fsync(req->file); #endif } -static ssize_t uv__fs_fsync(uv_fs_t* req) { -#if defined(__APPLE__) - /* See the comment in uv__fs_fdatasync. */ - return fcntl(req->file, F_FULLFSYNC); +static ssize_t uv__fs_fdatasync(uv_fs_t* req) { +#if defined(__linux__) || defined(__sun) || defined(__NetBSD__) + return fdatasync(req->file); +#elif defined(__APPLE__) + /* See the comment in uv__fs_fsync. */ + return uv__fs_fsync(req); #else return fsync(req->file); #endif @@ -442,7 +455,12 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) { return -1; } +#if defined(__MVS__) + len = os390_readlink(req->path, buf, len); +#else len = readlink(req->path, buf, len); +#endif + if (len == -1) { uv__free(buf); @@ -776,6 +794,118 @@ done: return r; } +static ssize_t uv__fs_copyfile(uv_fs_t* req) { +#if defined(__APPLE__) && !TARGET_OS_IPHONE + /* On macOS, use the native copyfile(3). */ + copyfile_flags_t flags; + + flags = COPYFILE_ALL; + + if (req->flags & UV_FS_COPYFILE_EXCL) + flags |= COPYFILE_EXCL; + + return copyfile(req->path, req->new_path, NULL, flags); +#else + uv_fs_t fs_req; + uv_file srcfd; + uv_file dstfd; + struct stat statsbuf; + int dst_flags; + int result; + int err; + size_t bytes_to_send; + int64_t in_offset; + + dstfd = -1; + err = 0; + + /* Open the source file. */ + srcfd = uv_fs_open(NULL, &fs_req, req->path, O_RDONLY, 0, NULL); + uv_fs_req_cleanup(&fs_req); + + if (srcfd < 0) + return srcfd; + + /* Get the source file's mode. */ + if (fstat(srcfd, &statsbuf)) { + err = -errno; + goto out; + } + + dst_flags = O_WRONLY | O_CREAT | O_TRUNC; + + if (req->flags & UV_FS_COPYFILE_EXCL) + dst_flags |= O_EXCL; + + /* Open the destination file. */ + dstfd = uv_fs_open(NULL, + &fs_req, + req->new_path, + dst_flags, + statsbuf.st_mode, + NULL); + uv_fs_req_cleanup(&fs_req); + + if (dstfd < 0) { + err = dstfd; + goto out; + } + + if (fchmod(dstfd, statsbuf.st_mode) == -1) { + err = -errno; + goto out; + } + + bytes_to_send = statsbuf.st_size; + in_offset = 0; + while (bytes_to_send != 0) { + err = uv_fs_sendfile(NULL, + &fs_req, + dstfd, + srcfd, + in_offset, + bytes_to_send, + NULL); + uv_fs_req_cleanup(&fs_req); + if (err < 0) + break; + bytes_to_send -= fs_req.result; + in_offset += fs_req.result; + } + +out: + if (err < 0) + result = err; + else + result = 0; + + /* Close the source file. */ + err = uv__close_nocheckstdio(srcfd); + + /* Don't overwrite any existing errors. */ + if (err != 0 && result == 0) + result = err; + + /* Close the destination file if it is open. */ + if (dstfd >= 0) { + err = uv__close_nocheckstdio(dstfd); + + /* Don't overwrite any existing errors. */ + if (err != 0 && result == 0) + result = err; + + /* Remove the destination file if something went wrong. */ + if (result != 0) { + uv_fs_unlink(NULL, &fs_req, req->new_path, NULL); + /* Ignore the unlink return value, as an error already happened. */ + uv_fs_req_cleanup(&fs_req); + } + } + + return result; +#endif +} + static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_dev = src->st_dev; dst->st_mode = src->st_mode; @@ -956,6 +1086,7 @@ static void uv__fs_work(struct uv__work* w) { X(CHMOD, chmod(req->path, req->mode)); X(CHOWN, chown(req->path, req->uid, req->gid)); X(CLOSE, close(req->file)); + X(COPYFILE, uv__fs_copyfile(req)); X(FCHMOD, fchmod(req->file, req->mode)); X(FCHOWN, fchown(req->file, req->uid, req->gid)); X(FDATASYNC, uv__fs_fdatasync(req)); @@ -1196,10 +1327,11 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, unsigned int nbufs, int64_t off, uv_fs_cb cb) { + INIT(READ); + if (bufs == NULL || nbufs == 0) return -EINVAL; - INIT(READ); req->file = file; req->nbufs = nbufs; @@ -1334,10 +1466,11 @@ int uv_fs_write(uv_loop_t* loop, unsigned int nbufs, int64_t off, uv_fs_cb cb) { + INIT(WRITE); + if (bufs == NULL || nbufs == 0) return -EINVAL; - INIT(WRITE); req->file = file; req->nbufs = nbufs; @@ -1359,6 +1492,9 @@ int uv_fs_write(uv_loop_t* loop, void uv_fs_req_cleanup(uv_fs_t* req) { + if (req == NULL) + return; + /* Only necessary for asychronous requests, i.e., requests with a callback. * Synchronous ones don't copy their arguments and have req->path and * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP is the @@ -1377,3 +1513,20 @@ void uv_fs_req_cleanup(uv_fs_t* req) { uv__free(req->ptr); req->ptr = NULL; } + + +int uv_fs_copyfile(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + int flags, + uv_fs_cb cb) { + INIT(COPYFILE); + + if (flags & ~UV_FS_COPYFILE_EXCL) + return -EINVAL; + + PATH2; + req->flags = flags; + POST; +} diff --git a/Utilities/cmlibuv/src/unix/fsevents.c b/Utilities/cmlibuv/src/unix/fsevents.c index 643e233..3883740 100644 --- a/Utilities/cmlibuv/src/unix/fsevents.c +++ b/Utilities/cmlibuv/src/unix/fsevents.c @@ -230,6 +230,7 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, uv_loop_t* loop; uv__cf_loop_state_t* state; uv__fsevents_event_t* event; + FSEventStreamEventFlags flags; QUEUE head; loop = info; @@ -245,8 +246,10 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, /* Process and filter out events */ for (i = 0; i < numEvents; i++) { + flags = eventFlags[i]; + /* Ignore system events */ - if (eventFlags[i] & kFSEventsSystem) + if (flags & kFSEventsSystem) continue; path = paths[i]; @@ -271,6 +274,9 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, /* Ignore events with path equal to directory itself */ if (len == 0) continue; +#else + if (len == 0 && (flags & kFSEventStreamEventFlagItemIsDir)) + continue; #endif /* MAC_OS_X_VERSION_10_7 */ /* Do not emit events from subdirectories (without option set) */ @@ -291,12 +297,24 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, memset(event, 0, sizeof(*event)); memcpy(event->path, path, len + 1); + event->events = UV_RENAME; - if ((eventFlags[i] & kFSEventsModified) != 0 && - (eventFlags[i] & kFSEventsRenamed) == 0) +#ifdef MAC_OS_X_VERSION_10_7 + if (0 != (flags & kFSEventsModified) && + 0 == (flags & kFSEventsRenamed)) { + event->events = UV_CHANGE; + } +#else + if (0 != (flags & kFSEventsModified) && + 0 != (flags & kFSEventStreamEventFlagItemIsDir) && + 0 == (flags & kFSEventStreamEventFlagItemRenamed)) { event->events = UV_CHANGE; - else - event->events = UV_RENAME; + } + if (0 == (flags & kFSEventStreamEventFlagItemIsDir) && + 0 == (flags & kFSEventStreamEventFlagItemRenamed)) { + event->events = UV_CHANGE; + } +#endif /* MAC_OS_X_VERSION_10_7 */ QUEUE_INSERT_TAIL(&head, &event->member); } diff --git a/Utilities/cmlibuv/src/unix/getaddrinfo.c b/Utilities/cmlibuv/src/unix/getaddrinfo.c index 2049aea..0185971 100644 --- a/Utilities/cmlibuv/src/unix/getaddrinfo.c +++ b/Utilities/cmlibuv/src/unix/getaddrinfo.c @@ -32,6 +32,7 @@ #include <stddef.h> /* NULL */ #include <stdlib.h> #include <string.h> +#include <net/if.h> /* if_indextoname() */ /* EAI_* constants. */ #include <netdb.h> @@ -200,3 +201,32 @@ void uv_freeaddrinfo(struct addrinfo* ai) { if (ai) freeaddrinfo(ai); } + + +int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { + char ifname_buf[UV_IF_NAMESIZE]; + size_t len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + if (if_indextoname(ifindex, ifname_buf) == NULL) + return -errno; + + len = strnlen(ifname_buf, sizeof(ifname_buf)); + + if (*size <= len) { + *size = len + 1; + return UV_ENOBUFS; + } + + memcpy(buffer, ifname_buf, len); + buffer[len] = '\0'; + *size = len; + + return 0; +} + +int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) { + return uv_if_indextoname(ifindex, buffer, size); +} diff --git a/Utilities/cmlibuv/src/unix/ibmi.c b/Utilities/cmlibuv/src/unix/ibmi.c new file mode 100644 index 0000000..c19e2fc --- /dev/null +++ b/Utilities/cmlibuv/src/unix/ibmi.c @@ -0,0 +1,112 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <sys/time.h> +#include <unistd.h> +#include <fcntl.h> +#include <utmp.h> +#include <libgen.h> + +#include <sys/protosw.h> +#include <procinfo.h> +#include <sys/proc.h> +#include <sys/procfs.h> + +#include <ctype.h> + +#include <sys/mntctl.h> +#include <sys/vmount.h> +#include <limits.h> +#include <strings.h> +#include <sys/vnode.h> + +uint64_t uv_get_free_memory(void) { + return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); +} + + +uint64_t uv_get_total_memory(void) { + return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); +} + + +void uv_loadavg(double avg[3]) { + avg[0] = avg[1] = avg[2] = 0; + return; +} + + +int uv_resident_set_memory(size_t* rss) { + return UV_ENOSYS; +} + + +int uv_uptime(double* uptime) { + return UV_ENOSYS; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int numcpus, idx = 0; + uv_cpu_info_t* cpu_info; + + *cpu_infos = NULL; + *count = 0; + + numcpus = sysconf(_SC_NPROCESSORS_ONLN); + + *cpu_infos = uv__malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!*cpu_infos) { + return -ENOMEM; + } + + cpu_info = *cpu_infos; + for (idx = 0; idx < numcpus; idx++) { + cpu_info->speed = 0; + cpu_info->model = uv__strdup("unknown"); + cpu_info->cpu_times.user = 0; + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + cpu_info->cpu_times.nice = 0; + cpu_info++; + } + *count = numcpus; + + return 0; +} diff --git a/Utilities/cmlibuv/src/unix/internal.h b/Utilities/cmlibuv/src/unix/internal.h index e9f7908..9cc87f0 100644 --- a/Utilities/cmlibuv/src/unix/internal.h +++ b/Utilities/cmlibuv/src/unix/internal.h @@ -120,6 +120,12 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); # define UV__POLLRDHUP 0x2000 #endif +#ifdef POLLPRI +# define UV__POLLPRI POLLPRI +#else +# define UV__POLLPRI 0 +#endif + #if !defined(O_CLOEXEC) && defined(__FreeBSD__) /* * It may be that we are just missing `__POSIX_VISIBLE >= 200809`. @@ -155,6 +161,12 @@ enum { UV_LOOP_BLOCK_SIGPROF = 1 }; +/* flags of excluding ifaddr */ +enum { + UV__EXCLUDE_IFPHYS, + UV__EXCLUDE_IFADDR +}; + typedef enum { UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */ UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */ @@ -173,7 +185,8 @@ struct uv__stream_queued_fds_s { defined(__FreeBSD__) || \ defined(__FreeBSD_kernel__) || \ defined(__linux__) || \ - defined(__OpenBSD__) + defined(__OpenBSD__) || \ + defined(__NetBSD__) #define uv__cloexec uv__cloexec_ioctl #define uv__nonblock uv__nonblock_ioctl #else diff --git a/Utilities/cmlibuv/src/unix/kqueue.c b/Utilities/cmlibuv/src/unix/kqueue.c index 6bc60bb..5e89bdc 100644 --- a/Utilities/cmlibuv/src/unix/kqueue.c +++ b/Utilities/cmlibuv/src/unix/kqueue.c @@ -34,6 +34,17 @@ #include <fcntl.h> #include <time.h> +/* + * Required on + * - Until at least FreeBSD 11.0 + * - Older versions of Mac OS X + * + * http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp + */ +#ifndef EV_OOBAND +#define EV_OOBAND EV_FLAG1 +#endif + static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags); @@ -48,11 +59,12 @@ int uv__kqueue_init(uv_loop_t* loop) { } +#if defined(__APPLE__) static int uv__has_forked_with_cfrunloop; +#endif int uv__io_fork(uv_loop_t* loop) { int err; - uv__close(loop->backend_fd); loop->backend_fd = -1; err = uv__kqueue_init(loop); if (err) @@ -166,6 +178,16 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } } + if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) { + EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0); + + if (++nevents == ARRAY_SIZE(events)) { + if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) + abort(); + nevents = 0; + } + } + w->events = w->pevents; } @@ -275,6 +297,20 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } } + if (ev->filter == EV_OOBAND) { + if (w->pevents & UV__POLLPRI) { + revents |= UV__POLLPRI; + w->rcount = ev->data; + } else { + /* TODO batch up */ + struct kevent events[1]; + EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); + if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) + if (errno != ENOENT) + abort(); + } + } + if (ev->filter == EVFILT_WRITE) { if (w->pevents & POLLOUT) { revents |= POLLOUT; diff --git a/Utilities/cmlibuv/src/unix/linux-core.c b/Utilities/cmlibuv/src/unix/linux-core.c index 2866e93..4d480ce 100644 --- a/Utilities/cmlibuv/src/unix/linux-core.c +++ b/Utilities/cmlibuv/src/unix/linux-core.c @@ -388,7 +388,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { * free when we switch over to edge-triggered I/O. */ if (pe->events == POLLERR || pe->events == POLLHUP) - pe->events |= w->pevents & (POLLIN | POLLOUT); + pe->events |= w->pevents & (POLLIN | POLLOUT | UV__POLLPRI); if (pe->events != 0) { /* Run signal watchers last. This also affects child process watchers @@ -837,7 +837,7 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { uv__free(cpu_infos); } -static int uv__ifaddr_exclude(struct ifaddrs *ent) { +static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) return 1; if (ent->ifa_addr == NULL) @@ -847,8 +847,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent) { * devices. We're not interested in this information yet. */ if (ent->ifa_addr->sa_family == PF_PACKET) - return 1; - return 0; + return exclude_type; + return !exclude_type; } int uv_interface_addresses(uv_interface_address_t** addresses, @@ -869,7 +869,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent)) + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) continue; (*count)++; @@ -887,7 +887,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, address = *addresses; for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent)) + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) continue; address->name = uv__strdup(ent->ifa_name); @@ -911,7 +911,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent)) + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) continue; address = *addresses; diff --git a/Utilities/cmlibuv/src/unix/loop.c b/Utilities/cmlibuv/src/unix/loop.c index bcd4924..5b5b0e0 100644 --- a/Utilities/cmlibuv/src/unix/loop.c +++ b/Utilities/cmlibuv/src/unix/loop.c @@ -31,7 +31,6 @@ int uv_loop_init(uv_loop_t* loop) { void* saved_data; int err; - uv__signal_global_once_init(); saved_data = loop->data; memset(loop, 0, sizeof(*loop)); @@ -68,6 +67,7 @@ int uv_loop_init(uv_loop_t* loop) { if (err) return err; + uv__signal_global_once_init(); err = uv_signal_init(loop, &loop->child_watcher); if (err) goto fail_signal_init; diff --git a/Utilities/cmlibuv/src/unix/netbsd.c b/Utilities/cmlibuv/src/unix/netbsd.c index 9b5546b..7425072 100644 --- a/Utilities/cmlibuv/src/unix/netbsd.c +++ b/Utilities/cmlibuv/src/unix/netbsd.c @@ -40,9 +40,16 @@ #include <unistd.h> #include <time.h> +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; static char *process_title; +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + int uv__platform_loop_init(uv_loop_t* loop) { return uv__kqueue_init(loop); } @@ -66,22 +73,32 @@ void uv_loadavg(double avg[3]) { int uv_exepath(char* buffer, size_t* size) { + /* Intermediate buffer, retrieving partial path name does not work + * As of NetBSD-8(beta), vnode->path translator does not handle files + * with longer names than 31 characters. + */ + char int_buf[PATH_MAX]; + size_t int_size; int mib[4]; - size_t cb; - pid_t mypid; if (buffer == NULL || size == NULL || *size == 0) return -EINVAL; - mypid = getpid(); mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; - mib[2] = mypid; - mib[3] = KERN_PROC_ARGV; + mib[2] = -1; + mib[3] = KERN_PROC_PATHNAME; + int_size = ARRAY_SIZE(int_buf); - cb = *size; - if (sysctl(mib, 4, buffer, &cb, NULL, 0)) + if (sysctl(mib, 4, int_buf, &int_size, NULL, 0)) return -errno; + + /* Copy string from the intermediate buffer to outer one with appropriate + * length. + */ + strlcpy(buffer, int_buf, *size); + + /* Set new size. */ *size = strlen(buffer); return 0; @@ -124,11 +141,24 @@ char** uv_setup_args(int argc, char** argv) { int uv_set_process_title(const char* title) { - if (process_title) uv__free(process_title); + char* new_title; + + new_title = uv__strdup(title); + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); - process_title = uv__strdup(title); + if (process_title == NULL) { + uv_mutex_unlock(&process_title_mutex); + return -ENOMEM; + } + + uv__free(process_title); + process_title = new_title; setproctitle("%s", title); + uv_mutex_unlock(&process_title_mutex); + return 0; } @@ -139,17 +169,24 @@ int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return -EINVAL; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + if (process_title) { len = strlen(process_title) + 1; - if (size < len) + if (size < len) { + uv_mutex_unlock(&process_title_mutex); return -ENOBUFS; + } memcpy(buffer, process_title, len); } else { len = 0; } + uv_mutex_unlock(&process_title_mutex); + buffer[len] = '\0'; return 0; diff --git a/Utilities/cmlibuv/src/unix/openbsd.c b/Utilities/cmlibuv/src/unix/openbsd.c index 56f0af1..c0ffa56 100644 --- a/Utilities/cmlibuv/src/unix/openbsd.c +++ b/Utilities/cmlibuv/src/unix/openbsd.c @@ -36,9 +36,16 @@ #include <unistd.h> +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; static char *process_title; +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + int uv__platform_loop_init(uv_loop_t* loop) { return uv__kqueue_init(loop); } @@ -146,9 +153,24 @@ char** uv_setup_args(int argc, char** argv) { int uv_set_process_title(const char* title) { + char* new_title; + + new_title = uv__strdup(title); + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (process_title == NULL) { + uv_mutex_unlock(&process_title_mutex); + return -ENOMEM; + } + uv__free(process_title); - process_title = uv__strdup(title); + process_title = new_title; setproctitle("%s", title); + + uv_mutex_unlock(&process_title_mutex); + return 0; } @@ -159,17 +181,24 @@ int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return -EINVAL; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + if (process_title) { len = strlen(process_title) + 1; - if (size < len) + if (size < len) { + uv_mutex_unlock(&process_title_mutex); return -ENOBUFS; + } memcpy(buffer, process_title, len); } else { len = 0; } + uv_mutex_unlock(&process_title_mutex); + buffer[len] = '\0'; return 0; diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.c b/Utilities/cmlibuv/src/unix/os390-syscalls.c index 7edf235..21558ea 100644 --- a/Utilities/cmlibuv/src/unix/os390-syscalls.c +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.c @@ -25,6 +25,8 @@ #include <stdlib.h> #include <assert.h> #include <search.h> +#include <termios.h> +#include <sys/msg.h> #define CW_CONDVAR 32 @@ -103,10 +105,19 @@ static void maybe_resize(uv__os390_epoll* lst, unsigned int len) { unsigned int newsize; unsigned int i; struct pollfd* newlst; + struct pollfd event; if (len <= lst->size) return; + if (lst->size == 0) + event.fd = -1; + else { + /* Extract the message queue at the end. */ + event = lst->items[lst->size - 1]; + lst->items[lst->size - 1].fd = -1; + } + newsize = next_power_of_two(len); newlst = uv__realloc(lst->items, newsize * sizeof(lst->items[0])); @@ -115,32 +126,101 @@ static void maybe_resize(uv__os390_epoll* lst, unsigned int len) { for (i = lst->size; i < newsize; ++i) newlst[i].fd = -1; + /* Restore the message queue at the end */ + newlst[newsize - 1] = event; + lst->items = newlst; lst->size = newsize; } +static void init_message_queue(uv__os390_epoll* lst) { + struct { + long int header; + char body; + } msg; + + /* initialize message queue */ + lst->msg_queue = msgget(IPC_PRIVATE, 0622 | IPC_CREAT); + if (lst->msg_queue == -1) + abort(); + + /* + On z/OS, the message queue will be affiliated with the process only + when a send is performed on it. Once this is done, the system + can be queried for all message queues belonging to our process id. + */ + msg.header = 1; + if (msgsnd(lst->msg_queue, &msg, sizeof(msg.body), 0) != 0) + abort(); + + /* Clean up the dummy message sent above */ + if (msgrcv(lst->msg_queue, &msg, sizeof(msg.body), 0, 0) != sizeof(msg.body)) + abort(); +} + + +static void before_fork(void) { + uv_mutex_lock(&global_epoll_lock); +} + + +static void after_fork(void) { + uv_mutex_unlock(&global_epoll_lock); +} + + +static void child_fork(void) { + QUEUE* q; + uv_once_t child_once = UV_ONCE_INIT; + + /* reset once */ + memcpy(&once, &child_once, sizeof(child_once)); + + /* reset epoll list */ + while (!QUEUE_EMPTY(&global_epoll_queue)) { + uv__os390_epoll* lst; + q = QUEUE_HEAD(&global_epoll_queue); + QUEUE_REMOVE(q); + lst = QUEUE_DATA(q, uv__os390_epoll, member); + uv__free(lst->items); + lst->items = NULL; + lst->size = 0; + } + + uv_mutex_unlock(&global_epoll_lock); + uv_mutex_destroy(&global_epoll_lock); +} + + static void epoll_init(void) { QUEUE_INIT(&global_epoll_queue); if (uv_mutex_init(&global_epoll_lock)) abort(); + + if (pthread_atfork(&before_fork, &after_fork, &child_fork)) + abort(); } uv__os390_epoll* epoll_create1(int flags) { uv__os390_epoll* lst; - uv_once(&once, epoll_init); - uv_mutex_lock(&global_epoll_lock); lst = uv__malloc(sizeof(*lst)); - if (lst == -1) - return NULL; - QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member); - uv_mutex_unlock(&global_epoll_lock); + if (lst != NULL) { + /* initialize list */ + lst->size = 0; + lst->items = NULL; + init_message_queue(lst); + maybe_resize(lst, 1); + lst->items[lst->size - 1].fd = lst->msg_queue; + lst->items[lst->size - 1].events = POLLIN; + uv_once(&once, epoll_init); + uv_mutex_lock(&global_epoll_lock); + QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member); + uv_mutex_unlock(&global_epoll_lock); + } - /* initialize list */ - lst->size = 0; - lst->items = NULL; return lst; } @@ -149,22 +229,32 @@ int epoll_ctl(uv__os390_epoll* lst, int op, int fd, struct epoll_event *event) { - if(op == EPOLL_CTL_DEL) { + uv_mutex_lock(&global_epoll_lock); + + if (op == EPOLL_CTL_DEL) { if (fd >= lst->size || lst->items[fd].fd == -1) { + uv_mutex_unlock(&global_epoll_lock); errno = ENOENT; return -1; } lst->items[fd].fd = -1; - } else if(op == EPOLL_CTL_ADD) { - maybe_resize(lst, fd + 1); + } else if (op == EPOLL_CTL_ADD) { + + /* Resizing to 'fd + 1' would expand the list to contain at least + * 'fd'. But we need to guarantee that the last index on the list + * is reserved for the message queue. So specify 'fd + 2' instead. + */ + maybe_resize(lst, fd + 2); if (lst->items[fd].fd != -1) { + uv_mutex_unlock(&global_epoll_lock); errno = EEXIST; return -1; } lst->items[fd].fd = fd; lst->items[fd].events = event->events; - } else if(op == EPOLL_CTL_MOD) { + } else if (op == EPOLL_CTL_MOD) { if (fd >= lst->size || lst->items[fd].fd == -1) { + uv_mutex_unlock(&global_epoll_lock); errno = ENOENT; return -1; } @@ -172,44 +262,36 @@ int epoll_ctl(uv__os390_epoll* lst, } else abort(); + uv_mutex_unlock(&global_epoll_lock); return 0; } int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, int maxevents, int timeout) { - size_t size; + nmsgsfds_t size; struct pollfd* pfds; int pollret; int reventcount; - uv_mutex_lock(&global_epoll_lock); - uv_mutex_unlock(&global_epoll_lock); - size = lst->size; + size = _SET_FDS_MSGS(size, 1, lst->size - 1); pfds = lst->items; pollret = poll(pfds, size, timeout); - if(pollret == -1) + if (pollret <= 0) return pollret; + pollret = _NFDS(pollret) + _NMSGS(pollret); + reventcount = 0; - for (int i = 0; i < lst->size && i < maxevents; ++i) { + for (int i = 0; + i < lst->size && i < maxevents && reventcount < pollret; ++i) { struct epoll_event ev; - ev.events = 0; - ev.fd = pfds[i].fd; - if(!pfds[i].revents) + if (pfds[i].fd == -1 || pfds[i].revents == 0) continue; - if(pfds[i].revents & POLLRDNORM) - ev.events = ev.events | POLLIN; - - if(pfds[i].revents & POLLWRNORM) - ev.events = ev.events | POLLOUT; - - if(pfds[i].revents & POLLHUP) - ev.events = ev.events | POLLHUP; - - pfds[i].revents = 0; + ev.fd = pfds[i].fd; + ev.events = pfds[i].revents; events[reventcount++] = ev; } @@ -235,9 +317,14 @@ int epoll_file_close(int fd) { } void epoll_queue_close(uv__os390_epoll* lst) { + /* Remove epoll instance from global queue */ uv_mutex_lock(&global_epoll_lock); QUEUE_REMOVE(&lst->member); uv_mutex_unlock(&global_epoll_lock); + + /* Free resources */ + msgctl(lst->msg_queue, IPC_RMID, NULL); + lst->msg_queue = -1; uv__free(lst->items); lst->items = NULL; } @@ -332,3 +419,81 @@ char* mkdtemp(char* path) { return path; } + + +ssize_t os390_readlink(const char* path, char* buf, size_t len) { + ssize_t rlen; + ssize_t vlen; + ssize_t plen; + char* delimiter; + char old_delim; + char* tmpbuf; + char realpathstr[PATH_MAX + 1]; + + tmpbuf = uv__malloc(len + 1); + if (tmpbuf == NULL) { + errno = ENOMEM; + return -1; + } + + rlen = readlink(path, tmpbuf, len); + if (rlen < 0) { + uv__free(tmpbuf); + return rlen; + } + + if (rlen < 3 || strncmp("/$", tmpbuf, 2) != 0) { + /* Straightforward readlink. */ + memcpy(buf, tmpbuf, rlen); + uv__free(tmpbuf); + return rlen; + } + + /* + * There is a parmlib variable at the beginning + * which needs interpretation. + */ + tmpbuf[rlen] = '\0'; + delimiter = strchr(tmpbuf + 2, '/'); + if (delimiter == NULL) + /* No slash at the end */ + delimiter = strchr(tmpbuf + 2, '\0'); + + /* Read real path of the variable. */ + old_delim = *delimiter; + *delimiter = '\0'; + if (realpath(tmpbuf, realpathstr) == NULL) { + uv__free(tmpbuf); + return -1; + } + + /* realpathstr is not guaranteed to end with null byte.*/ + realpathstr[PATH_MAX] = '\0'; + + /* Reset the delimiter and fill up the buffer. */ + *delimiter = old_delim; + plen = strlen(delimiter); + vlen = strlen(realpathstr); + rlen = plen + vlen; + if (rlen > len) { + uv__free(tmpbuf); + errno = ENAMETOOLONG; + return -1; + } + memcpy(buf, realpathstr, vlen); + memcpy(buf + vlen, delimiter, plen); + + /* Done using temporary buffer. */ + uv__free(tmpbuf); + + return rlen; +} + + +size_t strnlen(const char* str, size_t maxlen) { + void* p = memchr(str, 0, maxlen); + if (p == NULL) + return maxlen; + else + return p - str; +} diff --git a/Utilities/cmlibuv/src/unix/os390-syscalls.h b/Utilities/cmlibuv/src/unix/os390-syscalls.h index 61a7cee..6e34a88 100644 --- a/Utilities/cmlibuv/src/unix/os390-syscalls.h +++ b/Utilities/cmlibuv/src/unix/os390-syscalls.h @@ -50,6 +50,7 @@ typedef struct { QUEUE member; struct pollfd* items; unsigned long size; + int msg_queue; } uv__os390_epoll; /* epoll api */ @@ -65,5 +66,7 @@ int scandir(const char* maindir, struct dirent*** namelist, int (*compar)(const struct dirent **, const struct dirent **)); char *mkdtemp(char* path); +ssize_t os390_readlink(const char* path, char* buf, size_t len); +size_t strnlen(const char* str, size_t maxlen); #endif /* UV_OS390_SYSCALL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/os390.c b/Utilities/cmlibuv/src/unix/os390.c index 2ba5abf..081438e 100644 --- a/Utilities/cmlibuv/src/unix/os390.c +++ b/Utilities/cmlibuv/src/unix/os390.c @@ -25,6 +25,9 @@ #include <utmpx.h> #include <unistd.h> #include <sys/ps.h> +#include <builtins.h> +#include <termios.h> +#include <sys/msg.h> #if defined(__clang__) #include "csrsic.h" #else @@ -32,6 +35,7 @@ #endif #define CVT_PTR 0x10 +#define PSA_PTR 0x00 #define CSD_OFFSET 0x294 /* @@ -69,6 +73,18 @@ /* CPC model length from the CSRSI Service. */ #define CPCMODEL_LENGTH 16 +/* Pointer to the home (current) ASCB. */ +#define PSAAOLD 0x224 + +/* Pointer to rsm address space block extension. */ +#define ASCBRSME 0x16C + +/* + NUMBER OF FRAMES CURRENTLY IN USE BY THIS ADDRESS SPACE. + It does not include 2G frames. +*/ +#define RAXFMCT 0x2C + /* Thread Entry constants */ #define PGTH_CURRENT 1 #define PGTH_LEN 26 @@ -76,6 +92,9 @@ #pragma linkage(BPX4GTH, OS) #pragma linkage(BPX1GTH, OS) +/* TOD Clock resolution in nanoseconds */ +#define TOD_RES 4.096 + typedef unsigned data_area_ptr_assign_type; typedef union { @@ -100,7 +119,7 @@ void uv_loadavg(double avg[3]) { int uv__platform_loop_init(uv_loop_t* loop) { uv__os390_epoll* ep; - ep = epoll_create1(UV__EPOLL_CLOEXEC); + ep = epoll_create1(0); loop->ep = ep; if (ep == NULL) return -errno; @@ -118,9 +137,10 @@ void uv__platform_loop_delete(uv_loop_t* loop) { uint64_t uv__hrtime(uv_clocktype_t type) { - struct timeval time; - gettimeofday(&time, NULL); - return (uint64_t) time.tv_sec * 1e9 + time.tv_usec * 1e3; + unsigned long long timestamp; + __stckf(×tamp); + /* Convert to nanoseconds */ + return timestamp / TOD_RES; } @@ -337,13 +357,17 @@ uint64_t uv_get_total_memory(void) { int uv_resident_set_memory(size_t* rss) { - W_PSPROC buf; + char* psa; + char* ascb; + char* rax; + size_t nframes; - memset(&buf, 0, sizeof(buf)); - if (w_getpsent(0, &buf, sizeof(W_PSPROC)) == -1) - return -EINVAL; + psa = PSA_PTR; + ascb = *(char* __ptr32 *)(psa + PSAAOLD); + rax = *(char* __ptr32 *)(ascb + ASCBRSME); + nframes = *(unsigned int*)(rax + RAXFMCT); - *rss = buf.ps_size; + *rss = nframes * sysconf(_SC_PAGESIZE); return 0; } @@ -364,7 +388,6 @@ int uv_uptime(double* uptime) { int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { uv_cpu_info_t* cpu_info; - int result; int idx; siv1v2 info; data_area_ptr cvt = {0}; @@ -663,11 +686,124 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { return 0; } + +void uv__fs_event_close(uv_fs_event_t* handle) { + uv_fs_event_stop(handle); +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, + const char* filename, unsigned int flags) { + uv__os390_epoll* ep; + _RFIS reg_struct; + char* path; + int rc; + + if (uv__is_active(handle)) + return -EINVAL; + + ep = handle->loop->ep; + assert(ep->msg_queue != -1); + + reg_struct.__rfis_cmd = _RFIS_REG; + reg_struct.__rfis_qid = ep->msg_queue; + reg_struct.__rfis_type = 1; + memcpy(reg_struct.__rfis_utok, &handle, sizeof(handle)); + + path = uv__strdup(filename); + if (path == NULL) + return -ENOMEM; + + rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); + if (rc != 0) + return -errno; + + uv__handle_start(handle); + handle->path = path; + handle->cb = cb; + memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok, + sizeof(handle->rfis_rftok)); + + return 0; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + uv__os390_epoll* ep; + _RFIS reg_struct; + int rc; + + if (!uv__is_active(handle)) + return 0; + + ep = handle->loop->ep; + assert(ep->msg_queue != -1); + + reg_struct.__rfis_cmd = _RFIS_UNREG; + reg_struct.__rfis_qid = ep->msg_queue; + reg_struct.__rfis_type = 1; + memcpy(reg_struct.__rfis_rftok, handle->rfis_rftok, + sizeof(handle->rfis_rftok)); + + /* + * This call will take "/" as the path argument in case we + * don't care to supply the correct path. The system will simply + * ignore it. + */ + rc = __w_pioctl("/", _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); + if (rc != 0 && errno != EALREADY && errno != ENOENT) + abort(); + + uv__handle_stop(handle); + + return 0; +} + + +static int os390_message_queue_handler(uv__os390_epoll* ep) { + uv_fs_event_t* handle; + int msglen; + int events; + _RFIM msg; + + if (ep->msg_queue == -1) + return 0; + + msglen = msgrcv(ep->msg_queue, &msg, sizeof(msg), 0, IPC_NOWAIT); + + if (msglen == -1 && errno == ENOMSG) + return 0; + + if (msglen == -1) + abort(); + + events = 0; + if (msg.__rfim_event == _RFIM_ATTR || msg.__rfim_event == _RFIM_WRITE) + events = UV_CHANGE; + else if (msg.__rfim_event == _RFIM_RENAME) + events = UV_RENAME; + else + /* Some event that we are not interested in. */ + return 0; + + handle = *(uv_fs_event_t**)(msg.__rfim_utok); + handle->cb(handle, uv__basename_r(handle->path), events, 0); + return 1; +} + + void uv__io_poll(uv_loop_t* loop, int timeout) { static const int max_safe_timeout = 1789569; struct epoll_event events[1024]; struct epoll_event* pe; struct epoll_event e; + uv__os390_epoll* ep; int real_timeout; QUEUE* q; uv__io_t* w; @@ -745,9 +881,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { assert(timeout != -1); - timeout = real_timeout - timeout; - if (timeout > 0) + + if (timeout > 0) { + timeout = real_timeout - timeout; continue; + } return; } @@ -779,6 +917,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (fd == -1) continue; + ep = loop->ep; + if (fd == ep->msg_queue) { + os390_message_queue_handler(ep); + continue; + } + assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); @@ -843,7 +987,12 @@ void uv__set_process_title(const char* title) { } int uv__io_fork(uv_loop_t* loop) { - uv__platform_loop_delete(loop); + /* + Nullify the msg queue but don't close it because + it is still being used by the parent. + */ + loop->ep = NULL; + uv__platform_loop_delete(loop); return uv__platform_loop_init(loop); } diff --git a/Utilities/cmlibuv/src/unix/pipe.c b/Utilities/cmlibuv/src/unix/pipe.c index e3d436d..df3aad0 100644 --- a/Utilities/cmlibuv/src/unix/pipe.c +++ b/Utilities/cmlibuv/src/unix/pipe.c @@ -300,3 +300,56 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { else return uv__handle_type(handle->accepted_fd); } + + +int uv_pipe_chmod(uv_pipe_t* handle, int mode) { + unsigned desired_mode; + struct stat pipe_stat; + char* name_buffer; + size_t name_len; + int r; + + if (handle == NULL || uv__stream_fd(handle) == -1) + return -EBADF; + + if (mode != UV_READABLE && + mode != UV_WRITABLE && + mode != (UV_WRITABLE | UV_READABLE)) + return -EINVAL; + + if (fstat(uv__stream_fd(handle), &pipe_stat) == -1) + return -errno; + + desired_mode = 0; + if (mode & UV_READABLE) + desired_mode |= S_IRUSR | S_IRGRP | S_IROTH; + if (mode & UV_WRITABLE) + desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + /* Exit early if pipe already has desired mode. */ + if ((pipe_stat.st_mode & desired_mode) == desired_mode) + return 0; + + pipe_stat.st_mode |= desired_mode; + + /* Unfortunately fchmod does not work on all platforms, we will use chmod. */ + name_len = 0; + r = uv_pipe_getsockname(handle, NULL, &name_len); + if (r != UV_ENOBUFS) + return r; + + name_buffer = uv__malloc(name_len); + if (name_buffer == NULL) + return UV_ENOMEM; + + r = uv_pipe_getsockname(handle, name_buffer, &name_len); + if (r != 0) { + uv__free(name_buffer); + return r; + } + + r = chmod(name_buffer, pipe_stat.st_mode); + uv__free(name_buffer); + + return r != -1 ? 0 : -errno; +} diff --git a/Utilities/cmlibuv/src/unix/poll.c b/Utilities/cmlibuv/src/unix/poll.c index 370994b..816c7dc 100644 --- a/Utilities/cmlibuv/src/unix/poll.c +++ b/Utilities/cmlibuv/src/unix/poll.c @@ -33,8 +33,19 @@ static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { handle = container_of(w, uv_poll_t, io_watcher); - if (events & POLLERR) { - uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP); + /* + * As documented in the kernel source fs/kernfs/file.c #780 + * poll will return POLLERR|POLLPRI in case of sysfs + * polling. This does not happen in case of out-of-band + * TCP messages. + * + * The above is the case on (at least) FreeBSD and Linux. + * + * So to properly determine a POLLPRI or a POLLERR we need + * to check for both. + */ + if ((events & POLLERR) && !(events & UV__POLLPRI)) { + uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); uv__handle_stop(handle); handle->poll_cb(handle, -EBADF, 0); return; @@ -43,6 +54,8 @@ static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { pevents = 0; if (events & POLLIN) pevents |= UV_READABLE; + if (events & UV__POLLPRI) + pevents |= UV_PRIORITIZED; if (events & POLLOUT) pevents |= UV_WRITABLE; if (events & UV__POLLRDHUP) @@ -86,8 +99,9 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, static void uv__poll_stop(uv_poll_t* handle) { uv__io_stop(handle->loop, &handle->io_watcher, - POLLIN | POLLOUT | UV__POLLRDHUP); + POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); uv__handle_stop(handle); + uv__platform_invalidate_fd(handle->loop, handle->io_watcher.fd); } @@ -101,7 +115,8 @@ int uv_poll_stop(uv_poll_t* handle) { int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { int events; - assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); + assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT | + UV_PRIORITIZED)) == 0); assert(!uv__is_closing(handle)); uv__poll_stop(handle); @@ -112,6 +127,8 @@ int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { events = 0; if (pevents & UV_READABLE) events |= POLLIN; + if (pevents & UV_PRIORITIZED) + events |= UV__POLLPRI; if (pevents & UV_WRITABLE) events |= POLLOUT; if (pevents & UV_DISCONNECT) diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c index f2fe305..9842710 100644 --- a/Utilities/cmlibuv/src/unix/process.c +++ b/Utilities/cmlibuv/src/unix/process.c @@ -279,9 +279,12 @@ static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd) { + sigset_t set; int close_fd; int use_fd; + int err; int fd; + int n; if (options->flags & UV_PROCESS_DETACHED) setsid(); @@ -376,6 +379,31 @@ static void uv__process_child_init(const uv_process_options_t* options, environ = options->env; } + /* Reset signal disposition. Use a hard-coded limit because NSIG + * is not fixed on Linux: it's either 32, 34 or 64, depending on + * whether RT signals are enabled. We are not allowed to touch + * RT signal handlers, glibc uses them internally. + */ + for (n = 1; n < 32; n += 1) { + if (n == SIGKILL || n == SIGSTOP) + continue; /* Can't be changed. */ + + if (SIG_ERR != signal(n, SIG_DFL)) + continue; + + uv__write_int(error_fd, -errno); + _exit(127); + } + + /* Reset signal mask. */ + sigemptyset(&set); + err = pthread_sigmask(SIG_SETMASK, &set, NULL); + + if (err != 0) { + uv__write_int(error_fd, -err); + _exit(127); + } + execvp(options->file, options->args); uv__write_int(error_fd, -errno); _exit(127); @@ -391,6 +419,7 @@ int uv_spawn(uv_loop_t* loop, return -ENOSYS; #else int signal_pipe[2] = { -1, -1 }; + int pipes_storage[8][2]; int (*pipes)[2]; int stdio_count; ssize_t r; @@ -415,7 +444,10 @@ int uv_spawn(uv_loop_t* loop, stdio_count = 3; err = -ENOMEM; - pipes = uv__malloc(stdio_count * sizeof(*pipes)); + pipes = pipes_storage; + if (stdio_count > (int) ARRAY_SIZE(pipes_storage)) + pipes = uv__malloc(stdio_count * sizeof(*pipes)); + if (pipes == NULL) goto error; @@ -520,7 +552,9 @@ int uv_spawn(uv_loop_t* loop, process->pid = pid; process->exit_cb = options->exit_cb; - uv__free(pipes); + if (pipes != pipes_storage) + uv__free(pipes); + return exec_errorno; error: @@ -534,7 +568,9 @@ error: if (pipes[i][1] != -1) uv__close_nocheckstdio(pipes[i][1]); } - uv__free(pipes); + + if (pipes != pipes_storage) + uv__free(pipes); } return err; diff --git a/Utilities/cmlibuv/src/unix/proctitle.c b/Utilities/cmlibuv/src/unix/proctitle.c index 9160f7e..1b3a798 100644 --- a/Utilities/cmlibuv/src/unix/proctitle.c +++ b/Utilities/cmlibuv/src/unix/proctitle.c @@ -26,6 +26,8 @@ extern void uv__set_process_title(const char* title); +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; static void* args_mem; static struct { @@ -34,6 +36,11 @@ static struct { } process_title; +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + char** uv_setup_args(int argc, char** argv) { char** new_argv; size_t size; @@ -81,12 +88,16 @@ char** uv_setup_args(int argc, char** argv) { int uv_set_process_title(const char* title) { - if (process_title.len == 0) - return 0; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (process_title.len != 0) { + /* No need to terminate, byte after is always '\0'. */ + strncpy(process_title.str, title, process_title.len); + uv__set_process_title(title); + } - /* No need to terminate, byte after is always '\0'. */ - strncpy(process_title.str, title, process_title.len); - uv__set_process_title(title); + uv_mutex_unlock(&process_title_mutex); return 0; } @@ -95,12 +106,22 @@ int uv_set_process_title(const char* title) { int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return -EINVAL; - else if (size <= process_title.len) + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (size <= process_title.len) { + uv_mutex_unlock(&process_title_mutex); return -ENOBUFS; + } + + if (process_title.len != 0) + memcpy(buffer, process_title.str, process_title.len + 1); - memcpy(buffer, process_title.str, process_title.len + 1); buffer[process_title.len] = '\0'; + uv_mutex_unlock(&process_title_mutex); + return 0; } diff --git a/Utilities/cmlibuv/src/unix/pthread-barrier.c b/Utilities/cmlibuv/src/unix/pthread-barrier.c deleted file mode 100644 index b6e604d..0000000 --- a/Utilities/cmlibuv/src/unix/pthread-barrier.c +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright (c) 2016, Kari Tristan Helgason <kthelgason@gmail.com> - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -#include "uv-common.h" -#include "pthread-barrier.h" - -#include <stdlib.h> -#include <assert.h> - -/* TODO: support barrier_attr */ -int pthread_barrier_init(pthread_barrier_t* barrier, - const void* barrier_attr, - unsigned count) { - int rc; - _uv_barrier* b; - - if (barrier == NULL || count == 0) - return EINVAL; - - if (barrier_attr != NULL) - return ENOTSUP; - - b = uv__malloc(sizeof(*b)); - if (b == NULL) - return ENOMEM; - - b->in = 0; - b->out = 0; - b->threshold = count; - - if ((rc = pthread_mutex_init(&b->mutex, NULL)) != 0) - goto error2; - if ((rc = pthread_cond_init(&b->cond, NULL)) != 0) - goto error; - - barrier->b = b; - return 0; - -error: - pthread_mutex_destroy(&b->mutex); -error2: - uv__free(b); - return rc; -} - -int pthread_barrier_wait(pthread_barrier_t* barrier) { - int rc; - _uv_barrier* b; - - if (barrier == NULL || barrier->b == NULL) - return EINVAL; - - b = barrier->b; - /* Lock the mutex*/ - if ((rc = pthread_mutex_lock(&b->mutex)) != 0) - return rc; - - /* Increment the count. If this is the first thread to reach the threshold, - wake up waiters, unlock the mutex, then return - PTHREAD_BARRIER_SERIAL_THREAD. */ - if (++b->in == b->threshold) { - b->in = 0; - b->out = b->threshold - 1; - rc = pthread_cond_signal(&b->cond); - assert(rc == 0); - - pthread_mutex_unlock(&b->mutex); - return PTHREAD_BARRIER_SERIAL_THREAD; - } - /* Otherwise, wait for other threads until in is set to 0, - then return 0 to indicate this is not the first thread. */ - do { - if ((rc = pthread_cond_wait(&b->cond, &b->mutex)) != 0) - break; - } while (b->in != 0); - - /* mark thread exit */ - b->out--; - pthread_cond_signal(&b->cond); - pthread_mutex_unlock(&b->mutex); - return rc; -} - -int pthread_barrier_destroy(pthread_barrier_t* barrier) { - int rc; - _uv_barrier* b; - - if (barrier == NULL || barrier->b == NULL) - return EINVAL; - - b = barrier->b; - - if ((rc = pthread_mutex_lock(&b->mutex)) != 0) - return rc; - - if (b->in > 0 || b->out > 0) - rc = EBUSY; - - pthread_mutex_unlock(&b->mutex); - - if (rc) - return rc; - - pthread_cond_destroy(&b->cond); - pthread_mutex_destroy(&b->mutex); - uv__free(barrier->b); - barrier->b = NULL; - return 0; -} diff --git a/Utilities/cmlibuv/src/unix/stream.c b/Utilities/cmlibuv/src/unix/stream.c index 3857bc8..931e5f1 100644 --- a/Utilities/cmlibuv/src/unix/stream.c +++ b/Utilities/cmlibuv/src/unix/stream.c @@ -514,7 +514,7 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { int err; stream = container_of(w, uv_stream_t, io_watcher); - assert(events == POLLIN); + assert(events & POLLIN); assert(stream->accepted_fd == -1); assert(!(stream->flags & UV_CLOSING)); @@ -750,6 +750,7 @@ static void uv__write(uv_stream_t* stream) { int iovmax; int iovcnt; ssize_t n; + int err; start: @@ -782,14 +783,21 @@ start: */ if (req->send_handle) { + int fd_to_send; struct msghdr msg; struct cmsghdr *cmsg; - int fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle); union { char data[64]; struct cmsghdr alias; } scratch; + if (uv__is_closing(req->send_handle)) { + err = -EBADF; + goto error; + } + + fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle); + memset(&scratch, 0, sizeof(scratch)); assert(fd_to_send >= 0); @@ -851,15 +859,9 @@ start: } if (n < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - /* Error */ - req->error = -errno; - uv__write_req_finish(req); - uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); - if (!uv__io_active(&stream->io_watcher, POLLIN)) - uv__handle_stop(stream); - uv__stream_osx_interrupt_select(stream); - return; + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENOBUFS) { + err = -errno; + goto error; } else if (stream->flags & UV_STREAM_BLOCKING) { /* If this is a blocking stream, try again. */ goto start; @@ -923,6 +925,16 @@ start: /* Notify select() thread about state change */ uv__stream_osx_interrupt_select(stream); + + return; + +error: + req->error = err; + uv__write_req_finish(req); + uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); + if (!uv__io_active(&stream->io_watcher, POLLIN)) + uv__handle_stop(stream); + uv__stream_osx_interrupt_select(stream); } @@ -1249,8 +1261,9 @@ static void uv__read(uv_stream_t* stream) { int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { - assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE) && - "uv_shutdown (unix) only supports uv_handle_t right now"); + assert(stream->type == UV_TCP || + stream->type == UV_TTY || + stream->type == UV_NAMED_PIPE); if (!(stream->flags & UV_STREAM_WRITABLE) || stream->flags & UV_STREAM_SHUT || diff --git a/Utilities/cmlibuv/src/unix/sunos.c b/Utilities/cmlibuv/src/unix/sunos.c index 041f3f3..d6f9527 100644 --- a/Utilities/cmlibuv/src/unix/sunos.c +++ b/Utilities/cmlibuv/src/unix/sunos.c @@ -751,7 +751,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent) { return 1; if (ent->ifa_addr == NULL) return 1; - if (ent->ifa_addr->sa_family == PF_PACKET) + if (ent->ifa_addr->sa_family != AF_INET && + ent->ifa_addr->sa_family != AF_INET6) return 1; return 0; } @@ -760,7 +761,6 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { uv_interface_address_t* address; struct ifaddrs* addrs; struct ifaddrs* ent; - int i; if (getifaddrs(&addrs)) return -errno; diff --git a/Utilities/cmlibuv/src/unix/tcp.c b/Utilities/cmlibuv/src/unix/tcp.c index c423dcb..c7c8d21 100644 --- a/Utilities/cmlibuv/src/unix/tcp.c +++ b/Utilities/cmlibuv/src/unix/tcp.c @@ -28,15 +28,12 @@ #include <errno.h> -static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { +static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { + struct sockaddr_storage saddr; + socklen_t slen; int sockfd; int err; - if (domain == AF_UNSPEC || uv__stream_fd(handle) != -1) { - handle->flags |= flags; - return 0; - } - err = uv__socket(domain, SOCK_STREAM, 0); if (err < 0) return err; @@ -48,10 +45,74 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { return err; } + if (flags & UV_HANDLE_BOUND) { + /* Bind this new socket to an arbitrary port */ + slen = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + err = getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen); + if (err) { + uv__close(sockfd); + return err; + } + + err = bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen); + if (err) { + uv__close(sockfd); + return err; + } + } + return 0; } +static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { + struct sockaddr_storage saddr; + socklen_t slen; + + if (domain == AF_UNSPEC) { + handle->flags |= flags; + return 0; + } + + if (uv__stream_fd(handle) != -1) { + + if (flags & UV_HANDLE_BOUND) { + + if (handle->flags & UV_HANDLE_BOUND) { + /* It is already bound to a port. */ + handle->flags |= flags; + return 0; + } + + /* Query to see if tcp socket is bound. */ + slen = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) + return -errno; + + if ((saddr.ss_family == AF_INET6 && + ((struct sockaddr_in6*) &saddr)->sin6_port != 0) || + (saddr.ss_family == AF_INET && + ((struct sockaddr_in*) &saddr)->sin_port != 0)) { + /* Handle is already bound to a port. */ + handle->flags |= flags; + return 0; + } + + /* Bind to arbitrary port */ + if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) + return -errno; + } + + handle->flags |= flags; + return 0; + } + + return new_socket(handle, domain, flags); +} + + int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { int domain; @@ -260,6 +321,7 @@ int uv_tcp_getpeername(const uv_tcp_t* handle, int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { static int single_accept = -1; + unsigned long flags; int err; if (tcp->delayed_error) @@ -273,30 +335,17 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { if (single_accept) tcp->flags |= UV_TCP_SINGLE_ACCEPT; - err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE); - if (err) - return err; - -#ifdef __MVS__ + flags = UV_STREAM_READABLE; +#if defined(__MVS__) /* on zOS the listen call does not bind automatically if the socket is unbound. Hence the manual binding to an arbitrary port is required to be done manually */ - - if (!(tcp->flags & UV_HANDLE_BOUND)) { - struct sockaddr_storage saddr; - socklen_t slen = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - - if (getsockname(tcp->io_watcher.fd, (struct sockaddr*) &saddr, &slen)) - return -errno; - - if (bind(tcp->io_watcher.fd, (struct sockaddr*) &saddr, slen)) - return -errno; - - tcp->flags |= UV_HANDLE_BOUND; - } -#endif + flags |= UV_HANDLE_BOUND; +#endif + err = maybe_new_socket(tcp, AF_INET, flags); + if (err) + return err; if (listen(tcp->io_watcher.fd, backlog)) return -errno; diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c index a9b5e4c..abaca29 100644 --- a/Utilities/cmlibuv/src/unix/thread.c +++ b/Utilities/cmlibuv/src/unix/thread.c @@ -41,36 +41,159 @@ #define NANOSEC ((uint64_t) 1e9) -int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { - int err; - pthread_attr_t* attr; -#if defined(__APPLE__) - pthread_attr_t attr_storage; - struct rlimit lim; +#if defined(UV__PTHREAD_BARRIER_FALLBACK) +/* TODO: support barrier_attr */ +int pthread_barrier_init(pthread_barrier_t* barrier, + const void* barrier_attr, + unsigned count) { + int rc; + _uv_barrier* b; + + if (barrier == NULL || count == 0) + return EINVAL; + + if (barrier_attr != NULL) + return ENOTSUP; + + b = uv__malloc(sizeof(*b)); + if (b == NULL) + return ENOMEM; + + b->in = 0; + b->out = 0; + b->threshold = count; + + if ((rc = pthread_mutex_init(&b->mutex, NULL)) != 0) + goto error2; + if ((rc = pthread_cond_init(&b->cond, NULL)) != 0) + goto error; + + barrier->b = b; + return 0; + +error: + pthread_mutex_destroy(&b->mutex); +error2: + uv__free(b); + return rc; +} + +int pthread_barrier_wait(pthread_barrier_t* barrier) { + int rc; + _uv_barrier* b; + + if (barrier == NULL || barrier->b == NULL) + return EINVAL; + + b = barrier->b; + /* Lock the mutex*/ + if ((rc = pthread_mutex_lock(&b->mutex)) != 0) + return rc; + + /* Increment the count. If this is the first thread to reach the threshold, + wake up waiters, unlock the mutex, then return + PTHREAD_BARRIER_SERIAL_THREAD. */ + if (++b->in == b->threshold) { + b->in = 0; + b->out = b->threshold - 1; + rc = pthread_cond_signal(&b->cond); + assert(rc == 0); + + pthread_mutex_unlock(&b->mutex); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + /* Otherwise, wait for other threads until in is set to 0, + then return 0 to indicate this is not the first thread. */ + do { + if ((rc = pthread_cond_wait(&b->cond, &b->mutex)) != 0) + break; + } while (b->in != 0); + + /* mark thread exit */ + b->out--; + pthread_cond_signal(&b->cond); + pthread_mutex_unlock(&b->mutex); + return rc; +} + +int pthread_barrier_destroy(pthread_barrier_t* barrier) { + int rc; + _uv_barrier* b; + + if (barrier == NULL || barrier->b == NULL) + return EINVAL; + + b = barrier->b; + + if ((rc = pthread_mutex_lock(&b->mutex)) != 0) + return rc; + + if (b->in > 0 || b->out > 0) + rc = EBUSY; + + pthread_mutex_unlock(&b->mutex); + + if (rc) + return rc; + + pthread_cond_destroy(&b->cond); + pthread_mutex_destroy(&b->mutex); + uv__free(barrier->b); + barrier->b = NULL; + return 0; +} #endif - /* On OSX threads other than the main thread are created with a reduced stack - * size by default, adjust it to RLIMIT_STACK. - */ -#if defined(__APPLE__) - if (getrlimit(RLIMIT_STACK, &lim)) - abort(); - attr = &attr_storage; - if (pthread_attr_init(attr)) +/* On MacOS, threads other than the main thread are created with a reduced + * stack size by default. Adjust to RLIMIT_STACK aligned to the page size. + * + * On Linux, threads created by musl have a much smaller stack than threads + * created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency. + */ +static size_t thread_stack_size(void) { +#if defined(__APPLE__) || defined(__linux__) + struct rlimit lim; + + if (getrlimit(RLIMIT_STACK, &lim)) abort(); if (lim.rlim_cur != RLIM_INFINITY) { /* pthread_attr_setstacksize() expects page-aligned values. */ lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize(); - if (lim.rlim_cur >= PTHREAD_STACK_MIN) - if (pthread_attr_setstacksize(attr, lim.rlim_cur)) - abort(); + return lim.rlim_cur; } +#endif + +#if !defined(__linux__) + return 0; +#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__) + return 4 << 20; /* glibc default. */ #else - attr = NULL; + return 2 << 20; /* glibc default. */ #endif +} + + +int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { + int err; + size_t stack_size; + pthread_attr_t* attr; + pthread_attr_t attr_storage; + + attr = NULL; + stack_size = thread_stack_size(); + + if (stack_size > 0) { + attr = &attr_storage; + + if (pthread_attr_init(attr)) + abort(); + + if (pthread_attr_setstacksize(attr, stack_size)) + abort(); + } err = pthread_create(tid, attr, (void*(*)(void*)) entry, arg); @@ -118,6 +241,25 @@ int uv_mutex_init(uv_mutex_t* mutex) { } +int uv_mutex_init_recursive(uv_mutex_t* mutex) { + pthread_mutexattr_t attr; + int err; + + if (pthread_mutexattr_init(&attr)) + abort(); + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + abort(); + + err = pthread_mutex_init(mutex, &attr); + + if (pthread_mutexattr_destroy(&attr)) + abort(); + + return -err; +} + + void uv_mutex_destroy(uv_mutex_t* mutex) { if (pthread_mutex_destroy(mutex)) abort(); @@ -281,18 +423,20 @@ int uv_sem_trywait(uv_sem_t* sem) { int uv_sem_init(uv_sem_t* sem, unsigned int value) { uv_sem_t semid; - struct sembuf buf; int err; + union { + int val; + struct semid_ds* buf; + unsigned short* array; + } arg; - buf.sem_num = 0; - buf.sem_op = value; - buf.sem_flg = 0; semid = semget(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR); if (semid == -1) return -errno; - if (-1 == semop(semid, &buf, 1)) { + arg.val = value; + if (-1 == semctl(semid, 0, SETVAL, arg)) { err = errno; if (-1 == semctl(*sem, 0, IPC_RMID)) abort(); @@ -424,7 +568,7 @@ int uv_cond_init(uv_cond_t* cond) { if (err) return -err; -#if !(defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) +#if !(defined(__ANDROID_API__) && __ANDROID_API__ < 21) err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); if (err) goto error2; @@ -511,7 +655,8 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { timeout += uv__hrtime(UV_CLOCK_PRECISE); ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; -#if defined(__ANDROID__) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) +#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 + /* * The bionic pthread implementation doesn't support CLOCK_MONOTONIC, * but has this alternative function instead. @@ -519,7 +664,7 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { r = pthread_cond_timedwait_monotonic_np(cond, mutex, &ts); #else r = pthread_cond_timedwait(cond, mutex, &ts); -#endif /* __ANDROID__ */ +#endif /* __ANDROID_API__ */ #endif diff --git a/Utilities/cmlibuv/src/unix/tty.c b/Utilities/cmlibuv/src/unix/tty.c index ae1018f..fc8c3ab 100644 --- a/Utilities/cmlibuv/src/unix/tty.c +++ b/Utilities/cmlibuv/src/unix/tty.c @@ -48,6 +48,42 @@ static int uv__tty_is_slave(const int fd) { char dummy[256]; result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0; +#elif defined(__NetBSD__) + /* + * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the slave + * device name for both descriptors, the master one and slave one. + * + * Implement function to compare major device number with pts devices. + * + * The major numbers are machine-dependent, on NetBSD/amd64 they are + * respectively: + * - master tty: ptc - major 6 + * - slave tty: pts - major 5 + */ + + struct stat sb; + /* Lookup device's major for the pts driver and cache it. */ + static devmajor_t pts = NODEVMAJOR; + + if (pts == NODEVMAJOR) { + pts = getdevmajor("pts", S_IFCHR); + if (pts == NODEVMAJOR) + abort(); + } + + /* Lookup stat structure behind the file descriptor. */ + if (fstat(fd, &sb) != 0) + abort(); + + /* Assert character device. */ + if (!S_ISCHR(sb.st_mode)) + abort(); + + /* Assert valid major. */ + if (major(sb.st_rdev) == NODEVMAJOR) + abort(); + + result = (pts == major(sb.st_rdev)); #else /* Fallback to ptsname */ diff --git a/Utilities/cmlibuv/src/unix/udp.c b/Utilities/cmlibuv/src/unix/udp.c index c556325..a475bf5 100644 --- a/Utilities/cmlibuv/src/unix/udp.c +++ b/Utilities/cmlibuv/src/unix/udp.c @@ -237,8 +237,10 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { size = sendmsg(handle->io_watcher.fd, &h, 0); } while (size == -1 && errno == EINTR); - if (size == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) - break; + if (size == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + break; + } req->status = (size == -1 ? -errno : size); @@ -472,7 +474,7 @@ int uv__udp_try_send(uv_udp_t* handle, } while (size == -1 && errno == EINTR); if (size == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) return -EAGAIN; else return -errno; diff --git a/Utilities/cmlibuv/src/uv-data-getter-setters.c b/Utilities/cmlibuv/src/uv-data-getter-setters.c new file mode 100644 index 0000000..533e4a2 --- /dev/null +++ b/Utilities/cmlibuv/src/uv-data-getter-setters.c @@ -0,0 +1,96 @@ +#include "uv.h" + +const char* uv_handle_type_name(uv_handle_type type) { + switch (type) { +#define XX(uc,lc) case UV_##uc: return #lc; + UV_HANDLE_TYPE_MAP(XX) +#undef XX + case UV_FILE: return "file"; + case UV_HANDLE_TYPE_MAX: + case UV_UNKNOWN_HANDLE: return NULL; + } + return NULL; +} + +uv_handle_type uv_handle_get_type(const uv_handle_t* handle) { + return handle->type; +} + +void* uv_handle_get_data(const uv_handle_t* handle) { + return handle->data; +} + +uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle) { + return handle->loop; +} + +void uv_handle_set_data(uv_handle_t* handle, void* data) { + handle->data = data; +} + +const char* uv_req_type_name(uv_req_type type) { + switch (type) { +#define XX(uc,lc) case UV_##uc: return #lc; + UV_REQ_TYPE_MAP(XX) +#undef XX + case UV_REQ_TYPE_MAX: + case UV_UNKNOWN_REQ: return NULL; + } + return NULL; +} + +uv_req_type uv_req_get_type(const uv_req_t* req) { + return req->type; +} + +void* uv_req_get_data(const uv_req_t* req) { + return req->data; +} + +void uv_req_set_data(uv_req_t* req, void* data) { + req->data = data; +} + +size_t uv_stream_get_write_queue_size(const uv_stream_t* stream) { + return stream->write_queue_size; +} + +size_t uv_udp_get_send_queue_size(const uv_udp_t* handle) { + return handle->send_queue_size; +} + +size_t uv_udp_get_send_queue_count(const uv_udp_t* handle) { + return handle->send_queue_count; +} + +uv_pid_t uv_process_get_pid(const uv_process_t* proc) { + return proc->pid; +} + +uv_fs_type uv_fs_get_type(const uv_fs_t* req) { + return req->fs_type; +} + +ssize_t uv_fs_get_result(const uv_fs_t* req) { + return req->result; +} + +void* uv_fs_get_ptr(const uv_fs_t* req) { + return req->ptr; +} + +const char* uv_fs_get_path(const uv_fs_t* req) { + return req->path; +} + +uv_stat_t* uv_fs_get_statbuf(uv_fs_t* req) { + return &req->statbuf; +} + +void* uv_loop_get_data(const uv_loop_t* loop) { + return loop->data; +} + +void uv_loop_set_data(uv_loop_t* loop, void* data) { + loop->data = data; +} diff --git a/Utilities/cmlibuv/src/win/dl.c b/Utilities/cmlibuv/src/win/dl.c index 39e400a..97ac1c1 100644 --- a/Utilities/cmlibuv/src/win/dl.c +++ b/Utilities/cmlibuv/src/win/dl.c @@ -22,7 +22,7 @@ #include "uv.h" #include "internal.h" -static int uv__dlerror(uv_lib_t* lib, int errorno); +static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno); int uv_dlopen(const char* filename, uv_lib_t* lib) { @@ -37,12 +37,12 @@ int uv_dlopen(const char* filename, uv_lib_t* lib) { -1, filename_w, ARRAY_SIZE(filename_w))) { - return uv__dlerror(lib, GetLastError()); + return uv__dlerror(lib, filename, GetLastError()); } lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (lib->handle == NULL) { - return uv__dlerror(lib, GetLastError()); + return uv__dlerror(lib, filename, GetLastError()); } return 0; @@ -65,7 +65,7 @@ void uv_dlclose(uv_lib_t* lib) { int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) { *ptr = (void*) GetProcAddress(lib->handle, name); - return uv__dlerror(lib, *ptr ? 0 : GetLastError()); + return uv__dlerror(lib, "", *ptr ? 0 : GetLastError()); } @@ -88,31 +88,46 @@ static void uv__format_fallback_error(uv_lib_t* lib, int errorno){ -static int uv__dlerror(uv_lib_t* lib, int errorno) { +static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno) { + DWORD_PTR arg; DWORD res; + char* msg; if (lib->errmsg) { - LocalFree((void*)lib->errmsg); + LocalFree(lib->errmsg); lib->errmsg = NULL; } - if (errorno) { + if (errorno == 0) + return 0; + + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR) &lib->errmsg, 0, NULL); + + if (!res && GetLastError() == ERROR_MUI_FILE_NOT_FOUND) { res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - (LPSTR) &lib->errmsg, 0, NULL); - if (!res && GetLastError() == ERROR_MUI_FILE_NOT_FOUND) { - res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - 0, (LPSTR) &lib->errmsg, 0, NULL); - } - - if (!res) { - uv__format_fallback_error(lib, errorno); - } + 0, (LPSTR) &lib->errmsg, 0, NULL); + } + + if (res && errorno == ERROR_BAD_EXE_FORMAT && strstr(lib->errmsg, "%1")) { + msg = lib->errmsg; + lib->errmsg = NULL; + arg = (DWORD_PTR) filename; + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_FROM_STRING, + msg, + 0, 0, (LPSTR) &lib->errmsg, 0, (va_list*) &arg); + LocalFree(msg); } - return errorno ? -1 : 0; + if (!res) + uv__format_fallback_error(lib, errorno); + + return -1; } diff --git a/Utilities/cmlibuv/src/win/error.c b/Utilities/cmlibuv/src/win/error.c index 642d111..9b03bfe 100644 --- a/Utilities/cmlibuv/src/win/error.c +++ b/Utilities/cmlibuv/src/win/error.c @@ -58,7 +58,7 @@ void uv_fatal_error(const int errorno, const char* syscall) { LocalFree(buf); } - *((char*)NULL) = 0xff; /* Force debug break */ + DebugBreak(); abort(); } diff --git a/Utilities/cmlibuv/src/win/fs.c b/Utilities/cmlibuv/src/win/fs.c index 2d72cdc..097b00e 100644 --- a/Utilities/cmlibuv/src/win/fs.c +++ b/Utilities/cmlibuv/src/win/fs.c @@ -43,11 +43,26 @@ #define UV_FS_CLEANEDUP 0x0010 -#define QUEUE_FS_TP_JOB(loop, req) \ - do { \ - uv__req_register(loop, req); \ - uv__work_submit((loop), &(req)->work_req, uv__fs_work, uv__fs_done); \ - } while (0) +#define INIT(subtype) \ + do { \ + if (req == NULL) \ + return UV_EINVAL; \ + uv_fs_req_init(loop, req, subtype, cb); \ + } \ + while (0) + +#define POST \ + do { \ + if (cb != NULL) { \ + uv__req_register(loop, req); \ + uv__work_submit(loop, &req->work_req, uv__fs_work, uv__fs_done); \ + return 0; \ + } else { \ + uv__fs_work(&req->work_req); \ + return req->result; \ + } \ + } \ + while (0) #define SET_REQ_RESULT(req, result_value) \ do { \ @@ -113,6 +128,7 @@ const WCHAR LONG_PATH_PREFIX_LEN = 4; const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\"; const WCHAR UNC_PATH_PREFIX_LEN = 8; +static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; void uv_fs_init(void) { _fmode = _O_BINARY; @@ -220,6 +236,7 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path, INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, uv_fs_type fs_type, const uv_fs_cb cb) { + uv__once_init(); UV_REQ_INIT(req, UV_FS); req->loop = loop; req->flags = 0; @@ -398,21 +415,21 @@ void fs__open(uv_fs_t* req) { umask(current_umask); /* convert flags and mode to CreateFile parameters */ - switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { - case _O_RDONLY: + switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) { + case UV_FS_O_RDONLY: access = FILE_GENERIC_READ; break; - case _O_WRONLY: + case UV_FS_O_WRONLY: access = FILE_GENERIC_WRITE; break; - case _O_RDWR: + case UV_FS_O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; break; default: goto einval; } - if (flags & _O_APPEND) { + if (flags & UV_FS_O_APPEND) { access &= ~FILE_WRITE_DATA; access |= FILE_APPEND_DATA; } @@ -422,26 +439,33 @@ void fs__open(uv_fs_t* req) { * does. We indiscriminately use all the sharing modes, to match * UNIX semantics. In particular, this ensures that the file can * be deleted even whilst it's open, fixing issue #1449. + * We still support exclusive sharing mode, since it is necessary + * for opening raw block devices, otherwise Windows will prevent + * any attempt to write past the master boot record. */ - share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + if (flags & UV_FS_O_EXLOCK) { + share = 0; + } else { + share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + } - switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) { + switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) { case 0: - case _O_EXCL: + case UV_FS_O_EXCL: disposition = OPEN_EXISTING; break; - case _O_CREAT: + case UV_FS_O_CREAT: disposition = OPEN_ALWAYS; break; - case _O_CREAT | _O_EXCL: - case _O_CREAT | _O_TRUNC | _O_EXCL: + case UV_FS_O_CREAT | UV_FS_O_EXCL: + case UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL: disposition = CREATE_NEW; break; - case _O_TRUNC: - case _O_TRUNC | _O_EXCL: + case UV_FS_O_TRUNC: + case UV_FS_O_TRUNC | UV_FS_O_EXCL: disposition = TRUNCATE_EXISTING; break; - case _O_CREAT | _O_TRUNC: + case UV_FS_O_CREAT | UV_FS_O_TRUNC: disposition = CREATE_ALWAYS; break; default: @@ -449,34 +473,49 @@ void fs__open(uv_fs_t* req) { } attributes |= FILE_ATTRIBUTE_NORMAL; - if (flags & _O_CREAT) { + if (flags & UV_FS_O_CREAT) { if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) { attributes |= FILE_ATTRIBUTE_READONLY; } } - if (flags & _O_TEMPORARY ) { + if (flags & UV_FS_O_TEMPORARY ) { attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY; access |= DELETE; } - if (flags & _O_SHORT_LIVED) { + if (flags & UV_FS_O_SHORT_LIVED) { attributes |= FILE_ATTRIBUTE_TEMPORARY; } - switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) { + switch (flags & (UV_FS_O_SEQUENTIAL | UV_FS_O_RANDOM)) { case 0: break; - case _O_SEQUENTIAL: + case UV_FS_O_SEQUENTIAL: attributes |= FILE_FLAG_SEQUENTIAL_SCAN; break; - case _O_RANDOM: + case UV_FS_O_RANDOM: attributes |= FILE_FLAG_RANDOM_ACCESS; break; default: goto einval; } + if (flags & UV_FS_O_DIRECT) { + attributes |= FILE_FLAG_NO_BUFFERING; + } + + switch (flags & (UV_FS_O_DSYNC | UV_FS_O_SYNC)) { + case 0: + break; + case UV_FS_O_DSYNC: + case UV_FS_O_SYNC: + attributes |= FILE_FLAG_WRITE_THROUGH; + break; + default: + goto einval; + } + /* Setting this flag makes it possible to open a directory. */ attributes |= FILE_FLAG_BACKUP_SEMANTICS; @@ -489,9 +528,9 @@ void fs__open(uv_fs_t* req) { NULL); if (file == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); - if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) && - !(flags & _O_EXCL)) { - /* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */ + if (error == ERROR_FILE_EXISTS && (flags & UV_FS_O_CREAT) && + !(flags & UV_FS_O_EXCL)) { + /* Special case: when ERROR_FILE_EXISTS happens and UV_FS_O_CREAT was */ /* specified, it means the path referred to a directory. */ SET_REQ_UV_ERROR(req, UV_EISDIR, error); } else { @@ -556,9 +595,14 @@ void fs__read(uv_fs_t* req) { DWORD error; int result; unsigned int index; + LARGE_INTEGER original_position; + LARGE_INTEGER zero_offset; + int restore_position; VERIFY_FD(fd, req); + zero_offset.QuadPart = 0; + restore_position = 0; handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { @@ -569,6 +613,10 @@ void fs__read(uv_fs_t* req) { if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; + if (SetFilePointerEx(handle, zero_offset, &original_position, + FILE_CURRENT)) { + restore_position = 1; + } } else { overlapped_ptr = NULL; } @@ -593,6 +641,9 @@ void fs__read(uv_fs_t* req) { ++index; } while (result && index < req->fs.info.nbufs); + if (restore_position) + SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN); + if (result || bytes > 0) { SET_REQ_RESULT(req, bytes); } else { @@ -615,9 +666,14 @@ void fs__write(uv_fs_t* req) { DWORD bytes; int result; unsigned int index; + LARGE_INTEGER original_position; + LARGE_INTEGER zero_offset; + int restore_position; VERIFY_FD(fd, req); + zero_offset.QuadPart = 0; + restore_position = 0; handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); @@ -627,6 +683,10 @@ void fs__write(uv_fs_t* req) { if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; + if (SetFilePointerEx(handle, zero_offset, &original_position, + FILE_CURRENT)) { + restore_position = 1; + } } else { overlapped_ptr = NULL; } @@ -651,6 +711,9 @@ void fs__write(uv_fs_t* req) { ++index; } while (result && index < req->fs.info.nbufs); + if (restore_position) + SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN); + if (result || bytes > 0) { SET_REQ_RESULT(req, bytes); } else { @@ -1029,7 +1092,8 @@ cleanup: } -INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { +INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, + int do_lstat) { FILE_ALL_INFORMATION file_info; FILE_FS_VOLUME_INFORMATION volume_info; NTSTATUS nt_status; @@ -1084,19 +1148,25 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { */ statbuf->st_mode = 0; - if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + /* + * On Windows, FILE_ATTRIBUTE_REPARSE_POINT is a general purpose mechanism + * by which filesystem drivers can intercept and alter file system requests. + * + * The only reparse points we care about are symlinks and mount points, both + * of which are treated as POSIX symlinks. Further, we only care when + * invoked via lstat, which seeks information about the link instead of its + * target. Otherwise, reparse points must be treated as regular files. + */ + if (do_lstat && + (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { /* - * It is possible for a file to have FILE_ATTRIBUTE_REPARSE_POINT but not have - * any link data. In that case DeviceIoControl() in fs__readlink_handle() sets - * the last error to ERROR_NOT_A_REPARSE_POINT. Then the stat result mode - * calculated below will indicate a normal directory or file, as if - * FILE_ATTRIBUTE_REPARSE_POINT was not present. + * If reading the link fails, the reparse point is not a symlink and needs + * to be treated as a regular file. The higher level lstat function will + * detect this failure and retry without do_lstat if appropriate. */ - if (fs__readlink_handle(handle, NULL, &statbuf->st_size) == 0) { - statbuf->st_mode |= S_IFLNK; - } else if (GetLastError() != ERROR_NOT_A_REPARSE_POINT) { + if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) return -1; - } + statbuf->st_mode |= S_IFLNK; } if (statbuf->st_mode == 0) { @@ -1139,8 +1209,12 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { * * Therefore we'll just report a sensible value that's quite commonly okay * on modern hardware. + * + * 4096 is the minimum required to be compatible with newer Advanced Format + * drives (which have 4096 bytes per physical sector), and to be backwards + * compatible with older drives (which have 512 bytes per physical sector). */ - statbuf->st_blksize = 2048; + statbuf->st_blksize = 4096; /* Todo: set st_flags to something meaningful. Also provide a wrapper for * chattr(2). @@ -1191,9 +1265,11 @@ INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { return; } - if (fs__stat_handle(handle, &req->statbuf) != 0) { + if (fs__stat_handle(handle, &req->statbuf, do_lstat) != 0) { DWORD error = GetLastError(); - if (do_lstat && error == ERROR_SYMLINK_NOT_SUPPORTED) { + if (do_lstat && + (error == ERROR_SYMLINK_NOT_SUPPORTED || + error == ERROR_NOT_A_REPARSE_POINT)) { /* We opened a reparse point but it was not a symlink. Try again. */ fs__stat_impl(req, 0); @@ -1237,7 +1313,7 @@ static void fs__fstat(uv_fs_t* req) { return; } - if (fs__stat_handle(handle, &req->statbuf) != 0) { + if (fs__stat_handle(handle, &req->statbuf, 0) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } @@ -1309,6 +1385,22 @@ static void fs__ftruncate(uv_fs_t* req) { } +static void fs__copyfile(uv_fs_t* req) { + int flags; + int overwrite; + + flags = req->fs.info.file_flags; + overwrite = flags & UV_FS_COPYFILE_EXCL; + + if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) == 0) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + SET_REQ_RESULT(req, 0); +} + + static void fs__sendfile(uv_fs_t* req) { int fd_in = req->file.fd, fd_out = req->fs.info.fd_out; size_t length = req->fs.info.bufsml[0].len; @@ -1675,25 +1767,46 @@ error: static void fs__symlink(uv_fs_t* req) { - WCHAR* pathw = req->file.pathw; - WCHAR* new_pathw = req->fs.info.new_pathw; - int flags = req->fs.info.file_flags; - int result; + WCHAR* pathw; + WCHAR* new_pathw; + int flags; + int err; + pathw = req->file.pathw; + new_pathw = req->fs.info.new_pathw; - if (flags & UV_FS_SYMLINK_JUNCTION) { + if (req->fs.info.file_flags & UV_FS_SYMLINK_JUNCTION) { fs__create_junction(req, pathw, new_pathw); - } else if (pCreateSymbolicLinkW) { - result = pCreateSymbolicLinkW(new_pathw, - pathw, - flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1; - if (result == -1) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - } else { - SET_REQ_RESULT(req, result); - } - } else { + return; + } + if (!pCreateSymbolicLinkW) { SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); + return; + } + + if (req->fs.info.file_flags & UV_FS_SYMLINK_DIR) + flags = SYMBOLIC_LINK_FLAG_DIRECTORY | uv__file_symlink_usermode_flag; + else + flags = uv__file_symlink_usermode_flag; + + if (pCreateSymbolicLinkW(new_pathw, pathw, flags)) { + SET_REQ_RESULT(req, 0); + return; + } + + /* Something went wrong. We will test if it is because of user-mode + * symlinks. + */ + err = GetLastError(); + if (err == ERROR_INVALID_PARAMETER && + flags & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) { + /* This system does not support user-mode symlinks. We will clear the + * unsupported flag and retry. + */ + uv__file_symlink_usermode_flag = 0; + fs__symlink(req); + } else { + SET_REQ_WIN32_ERROR(req, err); } } @@ -1831,6 +1944,7 @@ static void uv__fs_work(struct uv__work* w) { XX(CLOSE, close) XX(READ, read) XX(WRITE, write) + XX(COPYFILE, copyfile) XX(SENDFILE, sendfile) XX(STAT, stat) XX(LSTAT, lstat) @@ -1877,6 +1991,9 @@ static void uv__fs_done(struct uv__work* w, int status) { void uv_fs_req_cleanup(uv_fs_t* req) { + if (req == NULL) + return; + if (req->flags & UV_FS_CLEANEDUP) return; @@ -1907,8 +2024,7 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_OPEN, cb); - + INIT(UV_FS_OPEN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); @@ -1916,28 +2032,14 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, req->fs.info.file_flags = flags; req->fs.info.mode = mode; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__open(req); - return req->result; - } + POST; } int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_CLOSE, cb); + INIT(UV_FS_CLOSE); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__close(req); - return req->result; - } + POST; } @@ -1948,11 +2050,11 @@ int uv_fs_read(uv_loop_t* loop, unsigned int nbufs, int64_t offset, uv_fs_cb cb) { + INIT(UV_FS_READ); + if (bufs == NULL || nbufs == 0) return UV_EINVAL; - uv_fs_req_init(loop, req, UV_FS_READ, cb); - req->file.fd = fd; req->fs.info.nbufs = nbufs; @@ -1966,14 +2068,7 @@ int uv_fs_read(uv_loop_t* loop, memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); req->fs.info.offset = offset; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__read(req); - return req->result; - } + POST; } @@ -1984,11 +2079,11 @@ int uv_fs_write(uv_loop_t* loop, unsigned int nbufs, int64_t offset, uv_fs_cb cb) { + INIT(UV_FS_WRITE); + if (bufs == NULL || nbufs == 0) return UV_EINVAL; - uv_fs_req_init(loop, req, UV_FS_WRITE, cb); - req->file.fd = fd; req->fs.info.nbufs = nbufs; @@ -2002,14 +2097,7 @@ int uv_fs_write(uv_loop_t* loop, memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); req->fs.info.offset = offset; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__write(req); - return req->result; - } + POST; } @@ -2017,20 +2105,13 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_UNLINK, cb); - + INIT(UV_FS_UNLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__unlink(req); - return req->result; - } + POST; } @@ -2038,22 +2119,14 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_MKDIR, cb); - + INIT(UV_FS_MKDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.mode = mode; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__mkdir(req); - return req->result; - } + POST; } @@ -2061,39 +2134,25 @@ int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_MKDTEMP, cb); - + INIT(UV_FS_MKDTEMP); err = fs__capture_path(req, tpl, NULL, TRUE); if (err) return uv_translate_sys_error(err); - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__mkdtemp(req); - return req->result; - } + POST; } int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_RMDIR, cb); - + INIT(UV_FS_RMDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__rmdir(req); - return req->result; - } + POST; } @@ -2101,22 +2160,14 @@ int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_SCANDIR, cb); - + INIT(UV_FS_SCANDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.file_flags = flags; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__scandir(req); - return req->result; - } + POST; } @@ -2124,20 +2175,13 @@ int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_LINK, cb); - + INIT(UV_FS_LINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__link(req); - return req->result; - } + POST; } @@ -2145,22 +2189,14 @@ int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_SYMLINK, cb); - + INIT(UV_FS_SYMLINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.file_flags = flags; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__symlink(req); - return req->result; - } + POST; } @@ -2168,20 +2204,13 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_READLINK, cb); - + INIT(UV_FS_READLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__readlink(req); - return req->result; - } + POST; } @@ -2189,24 +2218,18 @@ int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - if (!req || !path) { + INIT(UV_FS_REALPATH); + + if (!path) { return UV_EINVAL; } - uv_fs_req_init(loop, req, UV_FS_REALPATH, cb); - err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__realpath(req); - return req->result; - } + POST; } @@ -2214,88 +2237,53 @@ int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_CHOWN, cb); - + INIT(UV_FS_CHOWN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__chown(req); - return req->result; - } + POST; } int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FCHOWN, cb); - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fchown(req); - return req->result; - } + INIT(UV_FS_FCHOWN); + POST; } int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_STAT, cb); - + INIT(UV_FS_STAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__stat(req); - return req->result; - } + POST; } int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_LSTAT, cb); - + INIT(UV_FS_LSTAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__lstat(req); - return req->result; - } + POST; } int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FSTAT, cb); + INIT(UV_FS_FSTAT); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fstat(req); - return req->result; - } + POST; } @@ -2303,85 +2291,70 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_RENAME, cb); - + INIT(UV_FS_RENAME); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { return uv_translate_sys_error(err); } - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__rename(req); - return req->result; - } + POST; } int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FSYNC, cb); + INIT(UV_FS_FSYNC); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fsync(req); - return req->result; - } + POST; } int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FDATASYNC, cb); + INIT(UV_FS_FDATASYNC); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fdatasync(req); - return req->result; - } + POST; } int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int64_t offset, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FTRUNCATE, cb); - + INIT(UV_FS_FTRUNCATE); req->file.fd = fd; req->fs.info.offset = offset; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__ftruncate(req); - return req->result; - } + POST; } +int uv_fs_copyfile(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + int flags, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_COPYFILE); + + if (flags & ~UV_FS_COPYFILE_EXCL) + return UV_EINVAL; + + err = fs__capture_path(req, path, new_path, cb != NULL); + + if (err) + return uv_translate_sys_error(err); + + req->fs.info.file_flags = flags; + POST; +} + int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out, uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_SENDFILE, cb); - + INIT(UV_FS_SENDFILE); req->file.fd = fd_in; req->fs.info.fd_out = fd_out; req->fs.info.offset = in_offset; req->fs.info.bufsml[0].len = length; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__sendfile(req); - return req->result; - } + POST; } @@ -2392,21 +2365,13 @@ int uv_fs_access(uv_loop_t* loop, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_ACCESS, cb); - + INIT(UV_FS_ACCESS); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) return uv_translate_sys_error(err); req->fs.info.mode = flags; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } - - fs__access(req); - return req->result; + POST; } @@ -2414,39 +2379,23 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_CHMOD, cb); - + INIT(UV_FS_CHMOD); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); } req->fs.info.mode = mode; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__chmod(req); - return req->result; - } + POST; } int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FCHMOD, cb); - + INIT(UV_FS_FCHMOD); req->file.fd = fd; req->fs.info.mode = mode; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fchmod(req); - return req->result; - } + POST; } @@ -2454,8 +2403,7 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_UTIME, cb); - + INIT(UV_FS_UTIME); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { return uv_translate_sys_error(err); @@ -2463,30 +2411,15 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, req->fs.time.atime = atime; req->fs.time.mtime = mtime; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__utime(req); - return req->result; - } + POST; } int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, double mtime, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FUTIME, cb); - + INIT(UV_FS_FUTIME); req->file.fd = fd; req->fs.time.atime = atime; req->fs.time.mtime = mtime; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__futime(req); - return req->result; - } + POST; } diff --git a/Utilities/cmlibuv/src/win/getaddrinfo.c b/Utilities/cmlibuv/src/win/getaddrinfo.c index baab838..282d919 100644 --- a/Utilities/cmlibuv/src/win/getaddrinfo.c +++ b/Utilities/cmlibuv/src/win/getaddrinfo.c @@ -28,6 +28,8 @@ /* EAI_* constants. */ #include <winsock2.h> +/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */ +#include <iphlpapi.h> int uv__getaddrinfo_translate_error(int sys_err) { switch (sys_err) { @@ -73,6 +75,9 @@ int uv__getaddrinfo_translate_error(int sys_err) { /* Do we need different versions of this for different architectures? */ #define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) +#ifndef NDIS_IF_MAX_STRING_SIZE +#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE +#endif static void uv__getaddrinfo_work(struct uv__work* w) { uv_getaddrinfo_t* req; @@ -380,3 +385,69 @@ error: } return uv_translate_sys_error(err); } + +int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { + NET_LUID luid; + wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */ + DWORD bufsize; + int r; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + r = ConvertInterfaceIndexToLuid(ifindex, &luid); + + if (r != 0) + return uv_translate_sys_error(r); + + r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname)); + + if (r != 0) + return uv_translate_sys_error(r); + + /* Check how much space we need */ + bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL); + + if (bufsize == 0) { + return uv_translate_sys_error(GetLastError()); + } else if (bufsize > *size) { + *size = bufsize; + return UV_ENOBUFS; + } + + /* Convert to UTF-8 */ + bufsize = WideCharToMultiByte(CP_UTF8, + 0, + wname, + -1, + buffer, + *size, + NULL, + NULL); + + if (bufsize == 0) + return uv_translate_sys_error(GetLastError()); + + *size = bufsize - 1; + return 0; +} + +int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) { + int r; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + r = snprintf(buffer, *size, "%d", ifindex); + + if (r < 0) + return uv_translate_sys_error(r); + + if (r >= (int) *size) { + *size = r + 1; + return UV_ENOBUFS; + } + + *size = r; + return 0; +} diff --git a/Utilities/cmlibuv/src/win/handle.c b/Utilities/cmlibuv/src/win/handle.c index 72b49d9..3915070 100644 --- a/Utilities/cmlibuv/src/win/handle.c +++ b/Utilities/cmlibuv/src/win/handle.c @@ -152,3 +152,8 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { int uv_is_closing(const uv_handle_t* handle) { return !!(handle->flags & (UV__HANDLE_CLOSING | UV_HANDLE_CLOSED)); } + + +uv_os_fd_t uv_get_osfhandle(int fd) { + return uv__get_osfhandle(fd); +} diff --git a/Utilities/cmlibuv/src/win/internal.h b/Utilities/cmlibuv/src/win/internal.h index 83601d5..9f28f77 100644 --- a/Utilities/cmlibuv/src/win/internal.h +++ b/Utilities/cmlibuv/src/win/internal.h @@ -330,7 +330,6 @@ void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle); void uv__util_init(void); uint64_t uv__hrtime(double scale); -int uv_parent_pid(void); int uv_current_pid(void); __declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall); int uv__getpwuid_r(uv_passwd_t* pwd); diff --git a/Utilities/cmlibuv/src/win/pipe.c b/Utilities/cmlibuv/src/win/pipe.c index edf3002..1a7c4dc 100644 --- a/Utilities/cmlibuv/src/win/pipe.c +++ b/Utilities/cmlibuv/src/win/pipe.c @@ -31,6 +31,9 @@ #include "stream-inl.h" #include "req-inl.h" +#include <aclapi.h> +#include <accctrl.h> + typedef struct uv__ipc_queue_item_s uv__ipc_queue_item_t; struct uv__ipc_queue_item_s { @@ -202,7 +205,7 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, uv_unique_pipe_name(ptr, name, nameSize); pipeHandle = CreateNamedPipeA(name, - access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, + access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0, NULL); @@ -534,7 +537,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { */ handle->pipe.serv.accept_reqs[0].pipeHandle = CreateNamedPipeW(handle->name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | - FILE_FLAG_FIRST_PIPE_INSTANCE, + FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); @@ -803,7 +806,7 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, assert(req->pipeHandle == INVALID_HANDLE_VALUE); req->pipeHandle = CreateNamedPipeW(handle->name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); @@ -968,27 +971,31 @@ static DWORD WINAPI uv_pipe_zero_readfile_thread_proc(void* parameter) { uv_mutex_unlock(m); } restart_readfile: - result = ReadFile(handle->handle, - &uv_zero_, - 0, - &bytes, - NULL); - if (!result) { - err = GetLastError(); - if (err == ERROR_OPERATION_ABORTED && - handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { - if (handle->flags & UV_HANDLE_READING) { - /* just a brief break to do something else */ - handle->pipe.conn.readfile_thread = NULL; - /* resume after it is finished */ - uv_mutex_lock(m); - handle->pipe.conn.readfile_thread = hThread; - uv_mutex_unlock(m); - goto restart_readfile; - } else { - result = 1; /* successfully stopped reading */ + if (handle->flags & UV_HANDLE_READING) { + result = ReadFile(handle->handle, + &uv_zero_, + 0, + &bytes, + NULL); + if (!result) { + err = GetLastError(); + if (err == ERROR_OPERATION_ABORTED && + handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { + if (handle->flags & UV_HANDLE_READING) { + /* just a brief break to do something else */ + handle->pipe.conn.readfile_thread = NULL; + /* resume after it is finished */ + uv_mutex_lock(m); + handle->pipe.conn.readfile_thread = hThread; + uv_mutex_unlock(m); + goto restart_readfile; + } else { + result = 1; /* successfully stopped reading */ + } } } + } else { + result = 1; /* successfully aborted read before it even started */ } if (hThread) { assert(hThread == handle->pipe.conn.readfile_thread); @@ -1515,7 +1522,10 @@ static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error, static void uv_pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, int error, uv_buf_t buf) { - if (error == ERROR_BROKEN_PIPE) { + if (error == ERROR_OPERATION_ABORTED) { + /* do nothing (equivalent to EINTR) */ + } + else if (error == ERROR_BROKEN_PIPE) { uv_pipe_read_eof(loop, handle, buf); } else { uv_pipe_read_error(loop, handle, error, buf); @@ -1906,6 +1916,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { if (os_handle == INVALID_HANDLE_VALUE) return UV_EBADF; + uv__once_init(); /* In order to avoid closing a stdio file descriptor 0-2, duplicate the * underlying OS handle and forget about the original fd. * We could also opt to use the original OS handle and just never close it, @@ -1961,7 +1972,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { if (pipe->ipc) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); - pipe->pipe.conn.ipc_pid = uv_parent_pid(); + pipe->pipe.conn.ipc_pid = uv_os_getppid(); assert(pipe->pipe.conn.ipc_pid != -1); } return 0; @@ -1979,6 +1990,7 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) unsigned int name_len; int err; + uv__once_init(); name_info = NULL; if (handle->handle == INVALID_HANDLE_VALUE) { @@ -2123,3 +2135,80 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { else return UV_TCP; } + +int uv_pipe_chmod(uv_pipe_t* handle, int mode) { + SID_IDENTIFIER_AUTHORITY sid_world = SECURITY_WORLD_SID_AUTHORITY; + PACL old_dacl, new_dacl; + PSECURITY_DESCRIPTOR sd; + EXPLICIT_ACCESS ea; + PSID everyone; + int error; + + if (handle == NULL || handle->handle == INVALID_HANDLE_VALUE) + return UV_EBADF; + + if (mode != UV_READABLE && + mode != UV_WRITABLE && + mode != (UV_WRITABLE | UV_READABLE)) + return UV_EINVAL; + + if (!AllocateAndInitializeSid(&sid_world, + 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &everyone)) { + error = GetLastError(); + goto done; + } + + if (GetSecurityInfo(handle->handle, + SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, + NULL, + NULL, + &old_dacl, + NULL, + &sd)) { + error = GetLastError(); + goto clean_sid; + } + + memset(&ea, 0, sizeof(EXPLICIT_ACCESS)); + if (mode & UV_READABLE) + ea.grfAccessPermissions |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; + if (mode & UV_WRITABLE) + ea.grfAccessPermissions |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; + ea.grfAccessPermissions |= SYNCHRONIZE; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ea.Trustee.ptstrName = (LPTSTR)everyone; + + if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl)) { + error = GetLastError(); + goto clean_sd; + } + + if (SetSecurityInfo(handle->handle, + SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, + NULL, + NULL, + new_dacl, + NULL)) { + error = GetLastError(); + goto clean_dacl; + } + + error = 0; + +clean_dacl: + LocalFree((HLOCAL) new_dacl); +clean_sd: + LocalFree((HLOCAL) sd); +clean_sid: + FreeSid(everyone); +done: + return uv_translate_sys_error(error); +} diff --git a/Utilities/cmlibuv/src/win/process.c b/Utilities/cmlibuv/src/win/process.c index d141601..cc06d9e 100644 --- a/Utilities/cmlibuv/src/win/process.c +++ b/Utilities/cmlibuv/src/win/process.c @@ -405,8 +405,15 @@ static WCHAR* search_path(const WCHAR *file, /* Next slice starts just after where the previous one ended */ dir_start = dir_end; + /* If path is quoted, find quote end */ + if (*dir_start == L'"' || *dir_start == L'\'') { + dir_end = wcschr(dir_start + 1, *dir_start); + if (dir_end == NULL) { + dir_end = wcschr(dir_start, L'\0'); + } + } /* Slice until the next ; or \0 is found */ - dir_end = wcschr(dir_start, L';'); + dir_end = wcschr(dir_end, L';'); if (dir_end == NULL) { dir_end = wcschr(dir_start, L'\0'); } @@ -1051,15 +1058,18 @@ int uv_spawn(uv_loop_t* loop, startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); + process_flags = CREATE_UNICODE_ENVIRONMENT; + if (options->flags & UV_PROCESS_WINDOWS_HIDE) { /* Use SW_HIDE to avoid any potential process window. */ startup.wShowWindow = SW_HIDE; + + /* Hide console windows. */ + process_flags |= CREATE_NO_WINDOW; } else { startup.wShowWindow = SW_SHOWDEFAULT; } - process_flags = CREATE_UNICODE_ENVIRONMENT; - if (options->flags & UV_PROCESS_DETACHED) { /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That * means that libuv might not let you create a fully daemonized process @@ -1163,6 +1173,10 @@ int uv_spawn(uv_loop_t* loop, static int uv__kill(HANDLE process_handle, int signum) { + if (signum < 0 || signum >= NSIG) { + return UV_EINVAL; + } + switch (signum) { case SIGTERM: case SIGKILL: @@ -1227,8 +1241,15 @@ int uv_process_kill(uv_process_t* process, int signum) { int uv_kill(int pid, int signum) { int err; - HANDLE process_handle = OpenProcess(PROCESS_TERMINATE | - PROCESS_QUERY_INFORMATION, FALSE, pid); + HANDLE process_handle; + + if (pid == 0) { + process_handle = GetCurrentProcess(); + } else { + process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, + FALSE, + pid); + } if (process_handle == NULL) { err = GetLastError(); diff --git a/Utilities/cmlibuv/src/win/signal.c b/Utilities/cmlibuv/src/win/signal.c index 7b42dd9..a174da1 100644 --- a/Utilities/cmlibuv/src/win/signal.c +++ b/Utilities/cmlibuv/src/win/signal.c @@ -64,7 +64,7 @@ static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { } -RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare); +RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare) /* diff --git a/Utilities/cmlibuv/src/win/stream-inl.h b/Utilities/cmlibuv/src/win/stream-inl.h index bf12148..dba0374 100644 --- a/Utilities/cmlibuv/src/win/stream-inl.h +++ b/Utilities/cmlibuv/src/win/stream-inl.h @@ -36,6 +36,7 @@ INLINE static void uv_stream_init(uv_loop_t* loop, uv__handle_init(loop, (uv_handle_t*) handle, type); handle->write_queue_size = 0; handle->activecnt = 0; + handle->stream.conn.shutdown_req = NULL; } @@ -47,8 +48,6 @@ INLINE static void uv_connection_init(uv_stream_t* handle) { handle->read_req.event_handle = NULL; handle->read_req.wait_handle = INVALID_HANDLE_VALUE; handle->read_req.data = handle; - - handle->stream.conn.shutdown_req = NULL; } diff --git a/Utilities/cmlibuv/src/win/tcp.c b/Utilities/cmlibuv/src/win/tcp.c index 972539f..fd6efba 100644 --- a/Utilities/cmlibuv/src/win/tcp.c +++ b/Utilities/cmlibuv/src/win/tcp.c @@ -747,10 +747,15 @@ static int uv_tcp_try_connect(uv_connect_t* req, uv_connect_cb cb) { uv_loop_t* loop = handle->loop; const struct sockaddr* bind_addr; + struct sockaddr_storage converted; BOOL success; DWORD bytes; int err; + err = uv__convert_to_localhost_if_unspecified(addr, &converted); + if (err) + return err; + if (handle->delayed_error) { return handle->delayed_error; } @@ -782,12 +787,12 @@ static int uv_tcp_try_connect(uv_connect_t* req, memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); success = handle->tcp.conn.func_connectex(handle->socket, - addr, - addrlen, - NULL, - 0, - &bytes, - &req->u.io.overlapped); + (const struct sockaddr*) &converted, + addrlen, + NULL, + 0, + &bytes, + &req->u.io.overlapped); if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { /* Process the req without IOCP. */ @@ -1446,6 +1451,8 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { WSAPROTOCOL_INFOW protocol_info; int opt_len; int err; + struct sockaddr_storage saddr; + int saddr_len; /* Detect the address family of the socket. */ opt_len = (int) sizeof protocol_info; @@ -1466,6 +1473,19 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { return uv_translate_sys_error(err); } + /* Support already active socket. */ + saddr_len = sizeof(saddr); + if (!uv_tcp_getsockname(handle, (struct sockaddr*) &saddr, &saddr_len)) { + /* Socket is already bound. */ + handle->flags |= UV_HANDLE_BOUND; + saddr_len = sizeof(saddr); + if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) { + /* Socket is already connected. */ + uv_connection_init((uv_stream_t*) handle); + handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; + } + } + return 0; } diff --git a/Utilities/cmlibuv/src/win/thread.c b/Utilities/cmlibuv/src/win/thread.c index 91684e9..9eaad77 100644 --- a/Utilities/cmlibuv/src/win/thread.c +++ b/Utilities/cmlibuv/src/win/thread.c @@ -182,6 +182,7 @@ int uv_thread_join(uv_thread_t *tid) { else { CloseHandle(*tid); *tid = 0; + MemoryBarrier(); /* For feature parity with pthread_join(). */ return 0; } } @@ -198,6 +199,11 @@ int uv_mutex_init(uv_mutex_t* mutex) { } +int uv_mutex_init_recursive(uv_mutex_t* mutex) { + return uv_mutex_init(mutex); +} + + void uv_mutex_destroy(uv_mutex_t* mutex) { DeleteCriticalSection(mutex); } diff --git a/Utilities/cmlibuv/src/win/timer.c b/Utilities/cmlibuv/src/win/timer.c index 27ca771..7e006fe 100644 --- a/Utilities/cmlibuv/src/win/timer.c +++ b/Utilities/cmlibuv/src/win/timer.c @@ -56,7 +56,7 @@ static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) { } -RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare); +RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare) int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { diff --git a/Utilities/cmlibuv/src/win/tty.c b/Utilities/cmlibuv/src/win/tty.c index a6f5839..05a11e8 100644 --- a/Utilities/cmlibuv/src/win/tty.c +++ b/Utilities/cmlibuv/src/win/tty.c @@ -112,14 +112,30 @@ static int uv_tty_virtual_offset = -1; static int uv_tty_virtual_height = -1; static int uv_tty_virtual_width = -1; +/* The console window size + * We keep this separate from uv_tty_virtual_*. We use those values to only + * handle signalling SIGWINCH + */ + +static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE; +static int uv__tty_console_height = -1; +static int uv__tty_console_width = -1; + +static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param); +static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD dwEventThread, + DWORD dwmsEventTime); + /* We use a semaphore rather than a mutex or critical section because in some cases (uv__cancel_read_console) we need take the lock in the main thread and release it in another thread. Using a semaphore ensures that in such scenario the main thread will still block when trying to acquire the lock. */ static uv_sem_t uv_tty_output_lock; -static HANDLE uv_tty_output_handle = INVALID_HANDLE_VALUE; - static WORD uv_tty_default_text_attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; @@ -141,6 +157,18 @@ static void uv__determine_vterm_state(HANDLE handle); void uv_console_init(void) { if (uv_sem_init(&uv_tty_output_lock, 1)) abort(); + uv__tty_console_handle = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, + 0); + if (uv__tty_console_handle != NULL) { + QueueUserWorkItem(uv__tty_console_resize_message_loop_thread, + NULL, + WT_EXECUTELONGFUNCTION); + } } @@ -148,6 +176,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { HANDLE handle; CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; + uv__once_init(); handle = (HANDLE) uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) return UV_EBADF; @@ -183,11 +212,6 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { if (uv__vterm_state == UV_UNCHECKED) uv__determine_vterm_state(handle); - /* Store the global tty output handle. This handle is used by TTY read */ - /* streams to update the virtual window when a CONSOLE_BUFFER_SIZE_EVENT */ - /* is received. */ - uv_tty_output_handle = handle; - /* Remember the original console text attributes. */ uv_tty_capture_initial_style(&screen_buffer_info); @@ -704,25 +728,7 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, } records_left--; - /* If the window was resized, recompute the virtual window size. This */ - /* will trigger a SIGWINCH signal if the window size changed in an */ - /* way that matters to libuv. */ - if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) { - CONSOLE_SCREEN_BUFFER_INFO info; - - uv_sem_wait(&uv_tty_output_lock); - - if (uv_tty_output_handle != INVALID_HANDLE_VALUE && - GetConsoleScreenBufferInfo(uv_tty_output_handle, &info)) { - uv_tty_update_virtual_window(&info); - } - - uv_sem_post(&uv_tty_output_lock); - - continue; - } - - /* Ignore other events that are not key or resize events. */ + /* Ignore other events that are not key events. */ if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) { continue; } @@ -1102,9 +1108,6 @@ static int uv__cancel_read_console(uv_tty_t* handle) { static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { - int old_virtual_width = uv_tty_virtual_width; - int old_virtual_height = uv_tty_virtual_height; - uv_tty_virtual_width = info->dwSize.X; uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1; @@ -1124,14 +1127,6 @@ static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { if (uv_tty_virtual_offset < 0) { uv_tty_virtual_offset = 0; } - - /* If the virtual window size changed, emit a SIGWINCH signal. Don't emit */ - /* if this was the first time the virtual window size was computed. */ - if (old_virtual_width != -1 && old_virtual_height != -1 && - (uv_tty_virtual_width != old_virtual_width || - uv_tty_virtual_height != old_virtual_height)) { - uv__signal_dispatch(SIGWINCH); - } } @@ -2279,3 +2274,55 @@ static void uv__determine_vterm_state(HANDLE handle) { uv__vterm_state = UV_SUPPORTED; } + +static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) { + CONSOLE_SCREEN_BUFFER_INFO sb_info; + MSG msg; + + if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) + return 0; + + uv__tty_console_width = sb_info.dwSize.X; + uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + + if (pSetWinEventHook == NULL) + return 0; + + if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT, + EVENT_CONSOLE_LAYOUT, + NULL, + uv__tty_console_resize_event, + 0, + 0, + WINEVENT_OUTOFCONTEXT)) + return 0; + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +} + +static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD dwEventThread, + DWORD dwmsEventTime) { + CONSOLE_SCREEN_BUFFER_INFO sb_info; + int width, height; + + if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) + return; + + width = sb_info.dwSize.X; + height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + + if (width != uv__tty_console_width || height != uv__tty_console_height) { + uv__tty_console_width = width; + uv__tty_console_height = height; + uv__signal_dispatch(SIGWINCH); + } +} diff --git a/Utilities/cmlibuv/src/win/udp.c b/Utilities/cmlibuv/src/win/udp.c index 2fd15cf..cd1d0e0 100644 --- a/Utilities/cmlibuv/src/win/udp.c +++ b/Utilities/cmlibuv/src/win/udp.c @@ -897,13 +897,12 @@ int uv__udp_send(uv_udp_send_t* req, int err; if (!(handle->flags & UV_HANDLE_BOUND)) { - if (addrlen == sizeof(uv_addr_ip4_any_)) { + if (addrlen == sizeof(uv_addr_ip4_any_)) bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_; - } else if (addrlen == sizeof(uv_addr_ip6_any_)) { + else if (addrlen == sizeof(uv_addr_ip6_any_)) bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; - } else { - abort(); - } + else + return UV_EINVAL; err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); if (err) return uv_translate_sys_error(err); @@ -922,5 +921,45 @@ int uv__udp_try_send(uv_udp_t* handle, unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen) { - return UV_ENOSYS; + DWORD bytes; + const struct sockaddr* bind_addr; + struct sockaddr_storage converted; + int err; + + assert(nbufs > 0); + + err = uv__convert_to_localhost_if_unspecified(addr, &converted); + if (err) + return err; + + /* Already sending a message.*/ + if (handle->send_queue_count != 0) + return UV_EAGAIN; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + if (addrlen == sizeof(uv_addr_ip4_any_)) + bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_; + else if (addrlen == sizeof(uv_addr_ip6_any_)) + bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; + else + return UV_EINVAL; + err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); + if (err) + return uv_translate_sys_error(err); + } + + err = WSASendTo(handle->socket, + (WSABUF*)bufs, + nbufs, + &bytes, + 0, + (const struct sockaddr*) &converted, + addrlen, + NULL, + NULL); + + if (err) + return uv_translate_sys_error(WSAGetLastError()); + + return bytes; } diff --git a/Utilities/cmlibuv/src/win/util.c b/Utilities/cmlibuv/src/win/util.c index d2e7f77..3100bc2 100644 --- a/Utilities/cmlibuv/src/win/util.c +++ b/Utilities/cmlibuv/src/win/util.c @@ -331,7 +331,12 @@ uint64_t uv_get_total_memory(void) { } -int uv_parent_pid(void) { +uv_pid_t uv_os_getpid(void) { + return GetCurrentProcessId(); +} + + +uv_pid_t uv_os_getppid(void) { int parent_pid = -1; HANDLE handle; PROCESSENTRY32 pe; @@ -1388,7 +1393,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0) return uv_translate_sys_error(GetLastError()); - bufsize = sizeof(path); + bufsize = ARRAY_SIZE(path); if (!GetUserProfileDirectoryW(token, path, &bufsize)) { r = GetLastError(); CloseHandle(token); @@ -1403,7 +1408,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { CloseHandle(token); /* Get the username using GetUserNameW() */ - bufsize = sizeof(username); + bufsize = ARRAY_SIZE(username); if (!GetUserNameW(username, &bufsize)) { r = GetLastError(); diff --git a/Utilities/cmlibuv/src/win/winapi.c b/Utilities/cmlibuv/src/win/winapi.c index aa5d719..4ccdf0a 100644 --- a/Utilities/cmlibuv/src/win/winapi.c +++ b/Utilities/cmlibuv/src/win/winapi.c @@ -52,11 +52,15 @@ sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW; /* Powrprof.dll function pointer */ sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; +/* User32.dll function pointer */ +sSetWinEventHook pSetWinEventHook; + void uv_winapi_init(void) { HMODULE ntdll_module; HMODULE kernel32_module; HMODULE powrprof_module; + HMODULE user32_module; ntdll_module = GetModuleHandleA("ntdll.dll"); if (ntdll_module == NULL) { @@ -156,4 +160,10 @@ void uv_winapi_init(void) { GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification"); } + user32_module = LoadLibraryA("user32.dll"); + if (user32_module != NULL) { + pSetWinEventHook = (sSetWinEventHook) + GetProcAddress(user32_module, "SetWinEventHook"); + } + } diff --git a/Utilities/cmlibuv/src/win/winapi.h b/Utilities/cmlibuv/src/win/winapi.h index 4b0eeca..8993c65 100644 --- a/Utilities/cmlibuv/src/win/winapi.h +++ b/Utilities/cmlibuv/src/win/winapi.h @@ -4104,6 +4104,10 @@ # define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x00002000 #endif +#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x00000002 +#endif + /* from winternl.h */ typedef struct _UNICODE_STRING { USHORT Length; @@ -4730,6 +4734,25 @@ typedef DWORD (WINAPI *sPowerRegisterSuspendResumeNotification) HANDLE Recipient, _PHPOWERNOTIFY RegistrationHandle); +/* from Winuser.h */ +typedef VOID (CALLBACK* WINEVENTPROC) + (HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD idEventThread, + DWORD dwmsEventTime); + +typedef HWINEVENTHOOK (WINAPI *sSetWinEventHook) + (UINT eventMin, + UINT eventMax, + HMODULE hmodWinEventProc, + WINEVENTPROC lpfnWinEventProc, + DWORD idProcess, + DWORD idThread, + UINT dwflags); + /* Ntdll function pointers */ extern sRtlNtStatusToDosError pRtlNtStatusToDosError; @@ -4758,4 +4781,7 @@ extern sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW; /* Powrprof.dll function pointer */ extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; +/* User32.dll function pointer */ +extern sSetWinEventHook pSetWinEventHook; + #endif /* UV_WIN_WINAPI_H_ */ diff --git a/Utilities/cmlibuv/src/win/winsock.c b/Utilities/cmlibuv/src/win/winsock.c index e86d76b..7cfa90f 100644 --- a/Utilities/cmlibuv/src/win/winsock.c +++ b/Utilities/cmlibuv/src/win/winsock.c @@ -559,3 +559,31 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, return SOCKET_ERROR; } } + +int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr, + struct sockaddr_storage* storage) { + struct sockaddr_in* dest4; + struct sockaddr_in6* dest6; + + if (addr == NULL) + return UV_EINVAL; + + switch (addr->sa_family) { + case AF_INET: + dest4 = (struct sockaddr_in*) storage; + memcpy(dest4, addr, sizeof(*dest4)); + if (dest4->sin_addr.s_addr == 0) + dest4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + return 0; + case AF_INET6: + dest6 = (struct sockaddr_in6*) storage; + memcpy(dest6, addr, sizeof(*dest6)); + if (memcmp(&dest6->sin6_addr, + &uv_addr_ip6_any_.sin6_addr, + sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) + dest6->sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; + return 0; + default: + return UV_EINVAL; + } +} diff --git a/Utilities/cmlibuv/src/win/winsock.h b/Utilities/cmlibuv/src/win/winsock.h index 3115fe3..e8b274e 100644 --- a/Utilities/cmlibuv/src/win/winsock.h +++ b/Utilities/cmlibuv/src/win/winsock.h @@ -188,4 +188,7 @@ typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH { #endif +int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr, + struct sockaddr_storage* storage); + #endif /* UV_WIN_WINSOCK_H_ */ |