diff options
267 files changed, 7266 insertions, 932 deletions
diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt index dde4dbb..fc8c55e 100644 --- a/Help/command/FIND_XXX.txt +++ b/Help/command/FIND_XXX.txt @@ -89,7 +89,8 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: searched after paths from the current module, i.e. ``<CurrentPackage>_ROOT``, ``ENV{<CurrentPackage>_ROOT}``, ``<ParentPackage>_ROOT``, ``ENV{<ParentPackage>_ROOT}``, etc. - This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed. + This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting + the :variable:`CMAKE_FIND_USE_PACAKGE_ROOT_PATH` to ``FALSE``. See policy :policy:`CMP0074`. * |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX| @@ -97,7 +98,8 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: 2. Search paths specified in cmake-specific cache variables. These are intended to be used on the command line with a ``-DVAR=value``. The values are interpreted as :ref:`semicolon-separated lists <CMake Language Lists>`. - This can be skipped if ``NO_CMAKE_PATH`` is passed. + This can be skipped if ``NO_CMAKE_PATH`` is passed or by setting the + :variable:`CMAKE_FIND_USE_CMAKE_PATH` to ``FALSE``. * |CMAKE_PREFIX_PATH_XXX| * |CMAKE_XXX_PATH| @@ -107,7 +109,8 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: These are intended to be set in the user's shell configuration, and therefore use the host's native path separator (``;`` on Windows and ``:`` on UNIX). - This can be skipped if ``NO_CMAKE_ENVIRONMENT_PATH`` is passed. + This can be skipped if ``NO_CMAKE_ENVIRONMENT_PATH`` is passed or + by setting the :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH` to ``FALSE``. * |CMAKE_PREFIX_PATH_XXX| * |CMAKE_XXX_PATH| @@ -119,13 +122,16 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows: Hard-coded guesses should be specified with the ``PATHS`` option. 5. Search the standard system environment variables. - This can be skipped if ``NO_SYSTEM_ENVIRONMENT_PATH`` is an argument. + This can be skipped if ``NO_SYSTEM_ENVIRONMENT_PATH`` is passed or by + setting the :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` to ``FALSE``. * |SYSTEM_ENVIRONMENT_PATH_XXX| + * |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| 6. Search cmake variables defined in the Platform files for the current system. This can be skipped if ``NO_CMAKE_SYSTEM_PATH`` - is passed. + is passed or by setting the :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH` + to ``FALSE``. * |CMAKE_SYSTEM_PREFIX_PATH_XXX| * |CMAKE_SYSTEM_XXX_PATH| diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst index 46b9b63..884b2ee 100644 --- a/Help/command/add_test.rst +++ b/Help/command/add_test.rst @@ -7,7 +7,8 @@ Add a test to the project to be run by :manual:`ctest(1)`. add_test(NAME <name> COMMAND <command> [<arg>...] [CONFIGURATIONS <config>...] - [WORKING_DIRECTORY <dir>]) + [WORKING_DIRECTORY <dir>] + [COMMAND_EXPAND_LISTS]) Adds a test called ``<name>``. The test name may not contain spaces, quotes, or other characters special in CMake syntax. The options are: @@ -28,6 +29,11 @@ quotes, or other characters special in CMake syntax. The options are: directory set to the build directory corresponding to the current source directory. +``COMMAND_EXPAND_LISTS`` + Lists in ``COMMAND`` arguments will be expanded, including those + created with + :manual:`generator expressions <cmake-generator-expressions(7)>`. + The given test command is expected to exit with code ``0`` to pass and non-zero to fail, or vice-versa if the :prop_test:`WILL_FAIL` test property is set. Any output written to stdout or stderr will be diff --git a/Help/command/file.rst b/Help/command/file.rst index f99021e..666a532 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -13,6 +13,7 @@ Synopsis file(`STRINGS`_ <filename> <out-var> [...]) file(`\<HASH\> <HASH_>`_ <filename> <out-var>) file(`TIMESTAMP`_ <filename> <out-var> [...]) + file(`GET_RUNTIME_DEPENDENCIES`_ [...]) `Writing`_ file({`WRITE`_ | `APPEND`_} <filename> <content>...) @@ -130,6 +131,273 @@ timestamp variable will be set to the empty string (""). See the :command:`string(TIMESTAMP)` command for documentation of the ``<format>`` and ``UTC`` options. +.. _GET_RUNTIME_DEPENDENCIES: + +.. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + [RESOLVED_DEPENDENCIES_VAR <deps_var>] + [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>] + [CONFLICTING_DEPENDENICES_PREFIX <conflicting_deps_prefix>] + [EXECUTABLES [<executable_files>...]] + [LIBRARIES [<library_files>...]] + [MODULES [<module_files>...]] + [DIRECTORIES [<directories>...]] + [BUNDLE_EXECUTABLE <bundle_executable_file>] + [PRE_INCLUDE_REGEXES [<regexes>...]] + [PRE_EXCLUDE_REGEXES [<regexes>...]] + [POST_INCLUDE_REGEXES [<regexes>...]] + [POST_EXCLUDE_REGEXES [<regexes>...]] + ) + +Recursively get the list of libraries depended on by the given files. + +Please note that this sub-command is not intended to be used in project mode. +Instead, use it in an :command:`install(CODE)` or :command:`install(SCRIPT)` +block. For example: + +.. code-block:: cmake + + install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + # ... + ) + ]]) + +The arguments are as follows: + +``RESOLVED_DEPENDENCIES_VAR <deps_var>`` + Name of the variable in which to store the list of resolved dependencies. + +``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>`` + Name of the variable in which to store the list of unresolved dependencies. + If this variable is not specified, and there are any unresolved dependencies, + an error is issued. + +``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>`` + Variable prefix in which to store conflicting dependency information. + Dependencies are conflicting if two files with the same name are found in + two different directories. The list of filenames that conflict are stored in + ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list of paths + that were found for that filename are stored in + ``<conflicting_deps_prefix>_<filename>``. + +``EXECUTABLES <executable_files>`` + List of executable files to read for dependencies. These are executables that + are typically created with :command:`add_executable`, but they do not have to + be created by CMake. On Apple platforms, the paths to these files determine + the value of ``@executable_path`` when recursively resolving the libraries. + Specifying ``STATIC`` libraries, ``MODULE`` s, or ``SHARED`` libraries here + will result in undefined behavior. + +``LIBRARIES <library_files>`` + List of library files to read for dependencies. These are libraries that are + typically created with :command:`add_library(SHARED)`, but they do not have + to be created by CMake. Specifying ``STATIC`` libraries, ``MODULE`` s, or + executables here will result in undefined behavior. + +``MODULES <module_files>`` + List of loadable module files to read for dependencies. These are modules + that are typically created with :command:`add_library(MODULE)`, but they do + not have to be created by CMake. They are typically used by calling + ``dlopen()`` at runtime rather than linked at link time with ``ld -l``. + Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables here + will result in undefined behavior. + +``DIRECTORIES <directories>`` + List of additional directories to search for dependencies. On Linux + platforms, these directories are searched if the dependency is not found in + any of the other usual paths. If it is found in such a directory, a warning + is issued, because it means that the file is incomplete (it does not list all + of the directories that contain its dependencies.) On Windows platforms, + these directories are searched if the dependency is not found in any of the + other search paths, but no warning is issued, because searching other paths + is a normal part of Windows dependency resolution. On Apple platforms, this + argument has no effect. + +``BUNDLE_EXECTUBLE <bundle_executable_file>`` + Executable to treat as the "bundle executable" when resolving libraries. On + Apple platforms, this argument determines the value of ``@executable_path`` + when recursively resolving libraries for ``LIBRARIES`` and ``MODULES`` files. + It has no effect on ``EXECUTABLES`` files. On other platforms, it has no + effect. This is typically (but not always) one of the executables in the + ``EXECUTABLES`` argument which designates the "main" executable of the + package. + +The following arguments specify filters for including or excluding libraries to +be resolved. See below for a full description of how they work. + +``PRE_INCLUDE_REGEXES <regexes>`` + List of pre-include regexes through which to filter the names of + not-yet-resolved dependencies. + +``PRE_EXCLUDE_REGEXES <regexes>`` + List of pre-exclude regexes through which to filter the names of + not-yet-resolved dependencies. + +``POST_INCLUDE_REGEXES <regexes>`` + List of post-include regexes through which to filter the names of resolved + dependencies. + +``POST_EXCLUDE_REGEXES <regexes>`` + List of post-exclude regexes through which to filter the names of resolved + dependencies. + +These arguments can be used to blacklist unwanted system libraries when +resolving the dependencies, or to whitelist libraries from a specific +directory. The filtering works as follows: + +1. If the not-yet-resolved dependency matches any of the + ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency + resolution proceeds to step 4. +2. If the not-yet-resolved dependency matches any of the + ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency. +3. Otherwise, dependency resolution proceeds. +4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according to + the linking rules of the platform (see below). +5. If the dependency is found, and its full path matches one of the + ``POST_INCLUDE_REGEXES``, the full path is added to the resolved + dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves + that library's own dependencies. Otherwise, resolution proceeds to step 6. +6. If the dependency is found, but its full path matches one of the + ``POST_EXCLUDE_REGEXES``, it is not added to the resolved dependencies, and + dependency resolution stops for that dependency. +7. If the dependency is found, and its full path does not match either + ``POST_INCLUDE_REGEXES`` or ``POST_EXCLUDE_REGEXES``, the full path is added + to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` + recursively resolves that library's own dependencies. + +Different platforms have different rules for how dependencies are resolved. +These specifics are described here. + +On Linux platforms, library resolution works as follows: + +1. If the depending file does not have any ``RUNPATH`` entries, and the library + exists in one of the depending file's ``RPATH`` entries, or its parents', in + that order, the dependency is resolved to that file. +2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the + library exists in one of those entries, the dependency is resolved to that + file. +3. Otherwise, if the library exists in one of the directories listed by + ``ldconfig``, the dependency is resolved to that file. +4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries, the + dependency is resolved to that file. In this case, a warning is issued, + because finding a file in one of the ``DIRECTORIES`` means that the + depending file is not complete (it does not list all the directories from + which it pulls dependencies.) +5. Otherwise, the dependency is unresolved. + +On Windows platforms, library resolution works as follows: + +1. The dependent DLL name is converted to lowercase. Windows DLL names are + case-insensitive, and some linkers mangle the case of the DLL dependency + names. However, this makes it more difficult for ``PRE_INCLUDE_REGEXES``, + ``PRE_EXCLUDE_REGEXES``, ``POST_INCLUDE_REGEXES``, and + ``POST_EXCLUDE_REGEXES`` to properly filter DLL names - every regex would + have to check for both uppercase and lowercase letters. For example: + + .. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + # ... + PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$" + ) + + Converting the DLL name to lowercase allows the regexes to only match + lowercase names, thus simplifying the regex. For example: + + .. code-block:: cmake + + file(GET_RUNTIME_DEPENDENCIES + # ... + PRE_INCLUDE_REGEXES "^mylibrary\\.dll$" + ) + + This regex will match ``mylibrary.dll`` regardless of how it is cased, + either on disk or in the depending file. (For example, it will match + ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.) + + Please note that the directory portion of any resolved DLLs retains its + casing and is not converted to lowercase. Only the filename portion is + converted. + +2. (**Not yet implemented**) If the depending file is a Windows Store app, and + the dependency is listed as a dependency in the application's package + manifest, the dependency is resolved to that file. +3. Otherwise, if the library exists in the same directory as the depending + file, the dependency is resolved to that file. +4. Otherwise, if the library exists in either the operating system's + ``system32`` directory or the ``Windows`` directory, in that order, the + dependency is resolved to that file. +5. Otherwise, if the library exists in one of the directories specified by + ``DIRECTORIES``, in the order they are listed, the dependency is resolved to + that file. (In this case, a warning is not issued, because searching other + directories is a normal part of Windows library resolution.) +6. Otherwise, the dependency is unresolved. + +On Apple platforms, library resolution works as follows: + +1. If the dependency starts with ``@executable_path/``, and an ``EXECUTABLES`` + argument is in the process of being resolved, and replacing + ``@executable_path/`` with the directory of the executable yields an + existing file, the dependency is resolved to that file. +2. Otherwise, if the dependency starts with ``@executable_path/``, and there is + a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/`` with + the directory of the bundle executable yields an existing file, the + dependency is resolved to that file. +3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing + ``@loader_path/`` with the directory of the depending file yields an + existing file, the dependency is resolved to that file. +4. Otherwise, if the dependency starts with ``@rpath/``, and replacing + ``@rpath/`` with one of the ``RPATH`` entries of the depending file yields + an existing file, the dependency is resolved to that file. (Note that + ``RPATH`` entries that start with ``@executable_path/`` or ``@loader_path/`` + also have these items replaced with the appropriate path.) +5. Otherwise, if the dependency is an absolute file that exists, the dependency + is resolved to that file. +6. Otherwise, the dependency is unresolved. + +This function accepts several variables that determine which tool is used for +dependency resolution: + +.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM + + Determines which operating system and executable format the files are built + for. This could be one of several values: + + * ``linux+elf`` + * ``windows+pe`` + * ``macos+macho`` + + If this variable is not specified, it is determined automatically by system + introspection. + +.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL + + Determines the tool to use for dependency resolution. It could be one of + several values, depending on the value of + :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`: + + ================================================= ============================================= + ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`` ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL`` + ================================================= ============================================= + ``linux+elf`` ``objdump`` + ``windows+pe`` ``dumpbin`` + ``windows+pe`` ``objdump`` + ``macos+macho`` ``otool`` + ================================================= ============================================= + + If this variable is not specified, it is determined automatically by system + introspection. + +.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND + + Determines the path to the tool to use for dependency resolution. This is the + actual path to ``objdump``, ``dumpbin``, or ``otool``. + + If this variable is not specified, it is determined automatically by system + introspection. + Writing ^^^^^^^ diff --git a/Help/command/find_file.rst b/Help/command/find_file.rst index 2a14ad7..3f03f37 100644 --- a/Help/command/find_file.rst +++ b/Help/command/find_file.rst @@ -17,11 +17,10 @@ find_file .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_INCLUDE_PATH` .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_FRAMEWORK_PATH` -.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: Directories in ``INCLUDE``. - On Windows hosts: - ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` - is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|, and the - directories in ``PATH`` itself. +.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` and ``INCLUDE``. +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts: + ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` + is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` diff --git a/Help/command/find_library.rst b/Help/command/find_library.rst index 0861d67..8a55aca 100644 --- a/Help/command/find_library.rst +++ b/Help/command/find_library.rst @@ -17,11 +17,10 @@ find_library .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_LIBRARY_PATH` .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_FRAMEWORK_PATH` -.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: Directories in ``LIB``. - On Windows hosts: - ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` is set, - and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|, - and the directories in ``PATH`` itself. +.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` and ``INCLUDE``. +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts: + ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` + is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` is set, diff --git a/Help/command/find_path.rst b/Help/command/find_path.rst index 988a3fa..52ffe3c 100644 --- a/Help/command/find_path.rst +++ b/Help/command/find_path.rst @@ -17,11 +17,10 @@ find_path .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_INCLUDE_PATH` .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_FRAMEWORK_PATH` -.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: Directories in ``INCLUDE``. - On Windows hosts: - ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` - is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|, and the - directories in ``PATH`` itself. +.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` and ``INCLUDE``. +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts: + ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` + is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|. .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` diff --git a/Help/command/find_program.rst b/Help/command/find_program.rst index 4f00773..e2ff693 100644 --- a/Help/command/find_program.rst +++ b/Help/command/find_program.rst @@ -15,7 +15,8 @@ find_program .. |CMAKE_XXX_PATH| replace:: :variable:`CMAKE_PROGRAM_PATH` .. |CMAKE_XXX_MAC_PATH| replace:: :variable:`CMAKE_APPBUNDLE_PATH` -.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: ``PATH`` +.. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` itself. +.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts no extra search paths are included .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace:: |CMAKE_SYSTEM_PREFIX_PATH_XXX_SUBDIR| diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 1d023cb..bac1c6c 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.15 .. toctree:: :maxdepth: 1 + CMP0095: RPATH entries are properly escaped in the intermediary CMake install script. </policy/CMP0095> CMP0094: FindPython3, FindPython2 and FindPython use LOCATION for lookup strategy. </policy/CMP0094> CMP0093: FindBoost reports Boost_VERSION in x.y.z format. </policy/CMP0093> CMP0092: MSVC warning flags are not in CMAKE_{C,CXX}_FLAGS by default. </policy/CMP0092> diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 75ddd5d..cc6a7d2 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -180,6 +180,11 @@ Variables that Change Behavior /variable/CMAKE_FIND_ROOT_PATH_MODE_LIBRARY /variable/CMAKE_FIND_ROOT_PATH_MODE_PACKAGE /variable/CMAKE_FIND_ROOT_PATH_MODE_PROGRAM + /variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH + /variable/CMAKE_FIND_USE_CMAKE_PATH + /variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH + /variable/CMAKE_FIND_USE_PACAKGE_ROOT_PATH + /variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH /variable/CMAKE_FRAMEWORK_PATH /variable/CMAKE_IGNORE_PATH /variable/CMAKE_INCLUDE_DIRECTORIES_BEFORE diff --git a/Help/policy/CMP0095.rst b/Help/policy/CMP0095.rst new file mode 100644 index 0000000..4c56a05 --- /dev/null +++ b/Help/policy/CMP0095.rst @@ -0,0 +1,30 @@ +CMP0095 +------- + +``RPATH`` entries are properly escaped in the intermediary CMake install script. + +In CMake 3.15 and earlier, ``RPATH`` entries set via +:variable:`CMAKE_INSTALL_RPATH` or via :prop_tgt:`INSTALL_RPATH` have not been +escaped before being inserted into the ``cmake_install.cmake`` script. Dynamic +linkers on ELF-based systems (e.g. Linux and FreeBSD) allow certain keywords in +``RPATH`` entries, such as ``${ORIGIN}`` (More details are available in the +``ld.so`` man pages on those systems). The syntax of these keywords can match +CMake's variable syntax. In order to not be substituted (usually to an empty +string) already by the intermediary ``cmake_install.cmake`` script, the user had +to double-escape such ``RPATH`` keywords, e.g. +``set(CMAKE_INSTALL_RPATH "\\\${ORIGIN}/../lib")``. Since the intermediary +``cmake_install.cmake`` script is an implementation detail of CMake, CMake 3.16 +and later will make sure ``RPATH`` entries are inserted literally by escaping +any coincidental CMake syntax. + +The ``OLD`` behavior of this policy is to not escape ``RPATH`` entries in the +intermediary ``cmake_install.cmake`` script. The ``NEW`` behavior is to properly +escape coincidental CMake syntax in ``RPATH`` entries when generating the +intermediary ``cmake_install.cmake`` script. + +This policy was introduced in CMake version 3.16. CMake version |release| warns +when the policy is not set and detected usage of CMake-like syntax and uses +``OLD`` behavior. Use the :command:`cmake_policy` command to set it to ``OLD`` +or ``NEW`` explicitly. + +.. include:: DEPRECATED.txt diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index 3e6d560..f6dfabd 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst @@ -1,97 +1,247 @@ AUTOMOC ------- -Should the target be processed with automoc (for Qt projects). +Should the target be processed with auto-moc (for Qt projects). :prop_tgt:`AUTOMOC` is a boolean specifying whether CMake will handle the Qt ``moc`` preprocessor automatically, i.e. without having to use the -:module:`QT4_WRAP_CPP() <FindQt4>` or QT5_WRAP_CPP() macro. +:module:`QT4_WRAP_CPP() <FindQt4>` or ``QT5_WRAP_CPP()`` macro. Currently Qt4 and Qt5 are supported. +This property is initialized by the value of the :variable:`CMAKE_AUTOMOC` +variable if it is set when a target is created. + When this property is set ``ON``, CMake will scan the header and -source files at build time and invoke moc accordingly. +source files at build time and invoke ``moc`` accordingly. -* If an ``#include`` statement like ``#include "moc_<basename>.cpp"`` is found, - a macro from :prop_tgt:`AUTOMOC_MACRO_NAMES` is expected to appear in the - ``<basename>.h(xx)`` header file. ``moc`` is run on the header - file to generate ``moc_<basename>.cpp`` in the - ``<AUTOGEN_BUILD_DIR>/include`` directory which is automatically added - to the target's :prop_tgt:`INCLUDE_DIRECTORIES`. - This allows the compiler to find the included ``moc_<basename>.cpp`` file - regardless of the location the original source. - * For :prop_gbl:`multi configuration generators <GENERATOR_IS_MULTI_CONFIG>`, - the include directory is ``<AUTOGEN_BUILD_DIR>/include_<CONFIG>``. +Header file processing +^^^^^^^^^^^^^^^^^^^^^^ - * See :prop_tgt:`AUTOGEN_BUILD_DIR`. +At configuration time, a list of header files that should be scanned by +:prop_tgt:`AUTOMOC` is computed from the target's sources. -* If an ``#include`` statement like ``#include "<basename>.moc"`` is found, - a macro from :prop_tgt:`AUTOMOC_MACRO_NAMES` is expected to appear in the - source file and ``moc`` is run on the source file itself. +- All header files in the target's sources are added to the scan list. +- For all C++ source files ``<source_base>.<source_extension>`` in the + target's sources, CMake searches for -* Header files that are not included by an ``#include "moc_<basename>.cpp"`` - statement are nonetheless scanned for a macro out of - :prop_tgt:`AUTOMOC_MACRO_NAMES`. - The resulting ``moc_<basename>.cpp`` files are generated in custom - directories and automatically included in a generated - ``<AUTOGEN_BUILD_DIR>/mocs_compilation.cpp`` file, - which is compiled as part of the target. + - a regular header with the same base name + (``<source_base>.<header_extention>``) and + - a private header with the same base name and a ``_p`` suffix + (``<source_base>_p.<header_extention>``) - * The custom directories with checksum - based names help to avoid name collisions for ``moc`` files with the same - ``<basename>``. + and adds these to the scan list. - * See :prop_tgt:`AUTOGEN_BUILD_DIR`. +At build time, CMake scans each unknown or modified header file from the +list and searches for -* Additionally, header files with the same base name as a source file, - (like ``<basename>.h``) or ``_p`` appended to the base name (like - ``<basename>_p.h``), are scanned for a macro out of - :prop_tgt:`AUTOMOC_MACRO_NAMES`, and if found, ``moc`` - is also executed on those files. +- a Qt macro from :prop_tgt:`AUTOMOC_MACRO_NAMES`, +- additional file dependencies from the ``FILE`` argument of a + ``Q_PLUGIN_METADATA`` macro and +- additional file dependencies detected by filters defined in + :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. -* ``AUTOMOC`` always checks multiple header alternative extensions, - such as ``hpp``, ``hxx``, etc. when searching for headers. +If a Qt macro is found, then the header will be compiled by the ``moc`` to the +output file ``moc_<base_name>.cpp``. The complete output file path is +described in the section `Output file location`_. -* ``AUTOMOC`` looks for the ``Q_PLUGIN_METADATA`` macro and reruns the - ``moc`` when the file addressed by the ``FILE`` argument of the macro changes. +The header will be ``moc`` compiled again if a file from the additional file +dependencies changes. -This property is initialized by the value of the :variable:`CMAKE_AUTOMOC` -variable if it is set when a target is created. +Header ``moc`` output files ``moc_<base_name>.cpp`` can be included in source +files. In the section `Including header moc files in sources`_ there is more +information on that topic. + + +Source file processing +^^^^^^^^^^^^^^^^^^^^^^ + +At build time, CMake scans each unknown or modified C++ source file from the +target's sources for + +- a Qt macro from :prop_tgt:`AUTOMOC_MACRO_NAMES`, +- includes of header ``moc`` files + (see `Including header moc files in sources`_), +- additional file dependencies from the ``FILE`` argument of a + ``Q_PLUGIN_METADATA`` macro and +- additional file dependencies detected by filters defined in + :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. + +If a Qt macro is found, then the C++ source file +``<base>.<source_extension>`` is expected to as well contain an include +statement + +.. code-block:: c++ + + #include <<base>.moc> // or + #include "<base>.moc" + +The source file then will be compiled by the ``moc`` to the output file +``<base>.moc``. A description of the complete output file path is in section +`Output file location`_. + +The source will be ``moc`` compiled again if a file from the additional file +dependencies changes. + +Including header moc files in sources +""""""""""""""""""""""""""""""""""""" + +A source file can include the ``moc`` output file of a header +``<header_base>.<header_extension>`` by using an include statement of +the form + +.. code-block:: c++ + + #include <moc_<header_base>.cpp> // or + #include "moc_<header_base>.cpp" + +If the ``moc`` output file of a header is included by a source, it will +be generated in a different location than if it was not included. This is +described in the section `Output file location`_. + + +Output file location +^^^^^^^^^^^^^^^^^^^^ + +Included moc output files +""""""""""""""""""""""""" + +``moc`` output files that are included by a source file will be generated in + +- ``<AUTOGEN_BUILD_DIR>/include`` + for single configuration generators or in +- ``<AUTOGEN_BUILD_DIR>/include_<CONFIG>`` + for :prop_gbl:`multi configuration <GENERATOR_IS_MULTI_CONFIG>` generators. + +Where ``<AUTOGEN_BUILD_DIR>`` is the value of the target property +:prop_tgt:`AUTOGEN_BUILD_DIR`. + +The include directory is automatically added to the target's +:prop_tgt:`INCLUDE_DIRECTORIES`. + +Not included moc output files +""""""""""""""""""""""""""""" + +``moc`` output files that are not included in a source file will be generated +in + +- ``<AUTOGEN_BUILD_DIR>/<SOURCE_DIR_CHECKSUM>`` + for single configuration generators or in, +- ``<AUTOGEN_BUILD_DIR>/include_<CONFIG>/<SOURCE_DIR_CHECKSUM>`` + for :prop_gbl:`multi configuration <GENERATOR_IS_MULTI_CONFIG>` generators. + +Where ``<SOURCE_DIR_CHECKSUM>`` is a checksum computed from the relative +parent directory path of the ``moc`` input file. This scheme allows to have +``moc`` input files with the same name in different directories. + +All not included ``moc`` output files will be included automatically by the +CMake generated file + +- ``<AUTOGEN_BUILD_DIR>/mocs_compilation.cpp``, + +which is added to the target's sources. + + +Qt version detection +^^^^^^^^^^^^^^^^^^^^ + +:prop_tgt:`AUTOMOC` enabled targets need to know the Qt major and minor +version they're working with. The major version usually is provided by the +``INTERFACE_QT_MAJOR_VERSION`` property of the ``Qt[45]Core`` library, +that the target links to. To find the minor version, CMake builds a list of +available Qt versions from + +- ``Qt5Core_VERSION_MAJOR`` and ``Qt5Core_VERSION_MINOR`` variables + (usually set by ``find_package(Qt5...)``) +- ``Qt5Core_VERSION_MAJOR`` and ``Qt5Core_VERSION_MINOR`` directory properties +- ``QT_VERSION_MAJOR`` and ``QT_VERSION_MINOR`` variables + (usually set by ``find_package(Qt4...)``) +- ``QT_VERSION_MAJOR`` and ``QT_VERSION_MINOR`` directory properties + +in the context of the :command:`add_executable` or :command:`add_library` call. + +Assumed ``INTERFACE_QT_MAJOR_VERSION`` is a valid number, the first +entry in the list with a matching major version is taken. If no matching major +version was found, an error is generated. +If ``INTERFACE_QT_MAJOR_VERSION`` is not a valid number, the first +entry in the list is taken. + +A ``find_package(Qt[45]...)`` call sets the ``QT/Qt5Core_VERSION_MAJOR/MINOR`` +variables. If the call is in a different context than the +:command:`add_executable` or :command:`add_library` call, e.g. in a function, +then the version variables might not be available to the :prop_tgt:`AUTOMOC` +enabled target. +In that case the version variables can be forwarded from the +`find_package(Qt[45]...)` calling context to the :command:`add_executable` +or :command:`add_library` calling context as directory properties. +The following Qt5 example demonstrates the procedure. + +.. code-block:: cmake + + function (add_qt5_client) + find_package(Qt5 REQUIRED QUIET COMPONENTS Core Widgets) + ... + set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + PROPERTY Qt5Core_VERSION_MAJOR "${Qt5Core_VERSION_MAJOR}") + set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + PROPERTY Qt5Core_VERSION_MINOR "${Qt5Core_VERSION_MAJOR}") + ... + endfunction () + ... + add_qt5_client() + add_executable(myTarget main.cpp) + target_link_libraries(myTarget Qt5::QtWidgets) + set_property(TARGET myTarget PROPERTY AUTOMOC ON) + + +Modifiers +^^^^^^^^^ +:prop_tgt:`AUTOMOC_EXECUTABLE`: The ``moc`` executable will be detected automatically, but can be forced to -a certain binary using the :prop_tgt:`AUTOMOC_EXECUTABLE` property. +a certain binary using this target property. -Additional command line options for ``moc`` can be set via the -:prop_tgt:`AUTOMOC_MOC_OPTIONS` property. +:prop_tgt:`AUTOMOC_MOC_OPTIONS`: +Additional command line options for ``moc`` can be set in this target property. -By enabling the :variable:`CMAKE_AUTOMOC_RELAXED_MODE` variable the -rules for searching the files which will be processed by ``moc`` can be relaxed. -See the documentation for this variable for more details. +:prop_tgt:`AUTOMOC_MACRO_NAMES`: +This list of Qt macro names can be extended to search for additional macros in +headers and sources. -The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the -automoc targets together in an IDE, e.g. in MSVS. +:prop_tgt:`AUTOMOC_DEPEND_FILTERS`: +``moc`` dependency file names can be extracted from headers or sources by +defining file name filters in this target property. -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. +:prop_tgt:`AUTOMOC_COMPILER_PREDEFINES`: +Compiler pre definitions for ``moc`` are written to the ``moc_predefs.h`` file. +The generation of this file can be enabled or disabled in this target property. -Additional macro names to search for can be added to -:prop_tgt:`AUTOMOC_MACRO_NAMES`. +:prop_sf:`SKIP_AUTOMOC`: +Sources and headers can be excluded from :prop_tgt:`AUTOMOC` processing by +setting this source file property. -Additional ``moc`` dependency file names can be extracted from source code -by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. +:prop_sf:`SKIP_AUTOGEN`: +Source files can be excluded from :prop_tgt:`AUTOMOC`, +:prop_tgt:`AUTOUIC` and :prop_tgt:`AUTORCC` processing by +setting this source file property. -Compiler pre definitions for ``moc`` are written to a ``moc_predefs.h`` file -which is controlled by :prop_tgt:`AUTOMOC_COMPILER_PREDEFINES`. +:prop_gbl:`AUTOGEN_SOURCE_GROUP`: +This global property can be used to group files generated by +:prop_tgt:`AUTOMOC` or :prop_tgt:`AUTORCC` together in an IDE, e.g. in MSVS. -Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by -enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`. +:prop_gbl:`AUTOGEN_TARGETS_FOLDER`: +This global property can be used to group :prop_tgt:`AUTOMOC`, +:prop_tgt:`AUTOUIC` and :prop_tgt:`AUTORCC` targets together in an IDE, +e.g. in MSVS. -The number of parallel ``moc`` processes to start can be modified by -setting :prop_tgt:`AUTOGEN_PARALLEL`. +:variable:`CMAKE_GLOBAL_AUTOGEN_TARGET`: +A global ``autogen`` target, that depends on all :prop_tgt:`AUTOMOC` or +:prop_tgt:`AUTOUIC` generated ``<ORIGIN>_autogen`` targets in the project, +will be generated when this variable is ``ON``. -A global ``autogen`` target that depends on all :prop_tgt:`AUTOMOC` generated -``<ORIGIN>_autogen`` targets in the project can be generated by enabling -:variable:`CMAKE_GLOBAL_AUTOGEN_TARGET`. +:prop_tgt:`AUTOGEN_PARALLEL`: +This target property controls the number of ``moc`` or ``uic`` processes to +start in parallel during builds. See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/AUTORCC.rst b/Help/prop_tgt/AUTORCC.rst index 5db6ed0..cca3e58 100644 --- a/Help/prop_tgt/AUTORCC.rst +++ b/Help/prop_tgt/AUTORCC.rst @@ -1,7 +1,7 @@ AUTORCC ------- -Should the target be processed with autorcc (for Qt projects). +Should the target be processed with auto-rcc (for Qt projects). :prop_tgt:`AUTORCC` is a boolean specifying whether CMake will handle the Qt ``rcc`` code generator automatically, i.e. without having to use @@ -13,34 +13,49 @@ as target sources at build time and invoke ``rcc`` accordingly. This property is initialized by the value of the :variable:`CMAKE_AUTORCC` variable if it is set when a target is created. -By default :prop_tgt:`AUTORCC` is processed inside a +By default :prop_tgt:`AUTORCC` is processed by a :command:`custom command <add_custom_command>`. -If the ``.qrc`` file is :prop_sf:`GENERATED` though, a +If the ``.qrc`` file is :prop_sf:`GENERATED`, a :command:`custom target <add_custom_target>` is used instead. -Additional command line options for rcc can be set via the -:prop_sf:`AUTORCC_OPTIONS` source file property on the ``.qrc`` file. +When there are multiple ``.qrc`` files with the same name, CMake will +generate unspecified unique output file names for ``rcc``. Therefore, if +``Q_INIT_RESOURCE()`` or ``Q_CLEANUP_RESOURCE()`` need to be used, the +``.qrc`` file name must be unique. + + +Modifiers +^^^^^^^^^ +:prop_tgt:`AUTORCC_EXECUTABLE`: The ``rcc`` executable will be detected automatically, but can be forced to -a certain binary using the :prop_tgt:`AUTORCC_EXECUTABLE` property. +a certain binary by setting this target property. -The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group -the autorcc targets together in an IDE, e.g. in MSVS. +:prop_sf:`AUTORCC_OPTIONS`: +Additional command line options for ``rcc`` can be set via this ``.qrc`` +source file property. -The global property :prop_gbl:`AUTOGEN_SOURCE_GROUP` can be used to group -files generated by :prop_tgt:`AUTORCC` together in an IDE, e.g. in MSVS. +:prop_sf:`SKIP_AUTORCC`: +``.qrc`` files can be excluded from :prop_tgt:`AUTORCC` processing by +setting this source file property. -When there are multiple ``.qrc`` files with the same name, CMake will -generate unspecified unique names for ``rcc``. Therefore if -``Q_INIT_RESOURCE()`` or ``Q_CLEANUP_RESOURCE()`` need to be used the -``.qrc`` file name must be unique. +:prop_sf:`SKIP_AUTOGEN`: +Source files can be excluded from :prop_tgt:`AUTOMOC`, +:prop_tgt:`AUTOUIC` and :prop_tgt:`AUTORCC` processing by +setting this source file property. + +:prop_gbl:`AUTOGEN_SOURCE_GROUP`: +This global property can be used to group files generated by +:prop_tgt:`AUTOMOC` or :prop_tgt:`AUTORCC` together in an IDE, e.g. in MSVS. -Source files can be excluded from :prop_tgt:`AUTORCC` processing by -enabling :prop_sf:`SKIP_AUTORCC` or the broader :prop_sf:`SKIP_AUTOGEN`. +:prop_gbl:`AUTOGEN_TARGETS_FOLDER`: +This global property can be used to group :prop_tgt:`AUTOMOC`, +:prop_tgt:`AUTOUIC` and :prop_tgt:`AUTORCC` targets together in an IDE, +e.g. in MSVS. +:variable:`CMAKE_GLOBAL_AUTORCC_TARGET`: A global ``autorcc`` target that depends on all :prop_tgt:`AUTORCC` targets -in the project can be generated by enabling -:variable:`CMAKE_GLOBAL_AUTORCC_TARGET`. +in the project will be generated when this variable is ``ON``. 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 85226c1..5cf8755 100644 --- a/Help/prop_tgt/AUTOUIC.rst +++ b/Help/prop_tgt/AUTOUIC.rst @@ -1,47 +1,82 @@ AUTOUIC ------- -Should the target be processed with autouic (for Qt projects). +Should the target be processed with auto-uic (for Qt projects). :prop_tgt:`AUTOUIC` is a boolean specifying whether CMake will handle the Qt ``uic`` code generator automatically, i.e. without having to use the :module:`QT4_WRAP_UI() <FindQt4>` or ``QT5_WRAP_UI()`` macro. Currently Qt4 and Qt5 are supported. -When this property is ``ON``, CMake will scan the source files at build time -and invoke ``uic`` accordingly. If an ``#include`` statement like -``#include "ui_foo.h"`` is found in ``source.cpp``, a ``foo.ui`` file is -searched for first in the vicinity of ``source.cpp`` and afterwards in the -optional :prop_tgt:`AUTOUIC_SEARCH_PATHS` of the target. -``uic`` is run on the ``foo.ui`` file to generate ``ui_foo.h`` in the directory -``<AUTOGEN_BUILD_DIR>/include``, -which is automatically added to the target's :prop_tgt:`INCLUDE_DIRECTORIES`. +This property is initialized by the value of the :variable:`CMAKE_AUTOUIC` +variable if it is set when a target is created. -* For :prop_gbl:`multi configuration generators <GENERATOR_IS_MULTI_CONFIG>`, - the include directory is ``<AUTOGEN_BUILD_DIR>/include_<CONFIG>``. +When this property is ``ON``, CMake will scan the header and source files at +build time and invoke ``uic`` accordingly. -* See :prop_tgt:`AUTOGEN_BUILD_DIR`. -This property is initialized by the value of the :variable:`CMAKE_AUTOUIC` -variable if it is set when a target is created. +Header and source file processing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +At build time, CMake scans each header and source file from the +target's sources for include statements of the form + +.. code-block:: c++ + + #include "ui_<ui_base>.h" + +Once such an include statement is found in a file, CMake searches for the +``uic`` input file ``<ui_base>.ui`` + +- in the vicinity of the file and +- in the :prop_tgt:`AUTOUIC_SEARCH_PATHS` of the target. -Additional command line options for ``uic`` can be set via the -:prop_sf:`AUTOUIC_OPTIONS` source file property on the ``foo.ui`` file. -The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the -autouic targets together in an IDE, e.g. in MSVS. +If the ``<ui_base>.ui`` file was found, ``uic`` is called on it to generate +``ui_<ui_base>.h`` in the directory +- ``<AUTOGEN_BUILD_DIR>/include`` for single configuration generators or in +- ``<AUTOGEN_BUILD_DIR>/include_<CONFIG>`` for + :prop_gbl:`multi configuration <GENERATOR_IS_MULTI_CONFIG>` generators. + +Where ``<AUTOGEN_BUILD_DIR>`` is the value of the target property +:prop_tgt:`AUTOGEN_BUILD_DIR`. + +The include directory is automatically added to the target's +:prop_tgt:`INCLUDE_DIRECTORIES`. + + +Modifiers +^^^^^^^^^ + +:prop_tgt:`AUTOUIC_EXECUTABLE`: The ``uic`` executable will be detected automatically, but can be forced to -a certain binary using the :prop_tgt:`AUTOUIC_EXECUTABLE` property. +a certain binary using this target property. + +:prop_sf:`AUTOUIC_OPTIONS`: Additional command line options for ``uic`` can +be set via this source file property on a ``<base_name>.ui`` file. + +:prop_sf:`SKIP_AUTOUIC`: +Source files can be excluded from :prop_tgt:`AUTOUIC` processing by setting +this source file property. + +:prop_sf:`SKIP_AUTOGEN`: +Source files can be excluded from :prop_tgt:`AUTOMOC`, +:prop_tgt:`AUTOUIC` and :prop_tgt:`AUTORCC` processing by +setting this source file property. -Source files can be excluded from :prop_tgt:`AUTOUIC` processing by -enabling :prop_sf:`SKIP_AUTOUIC` or the broader :prop_sf:`SKIP_AUTOGEN`. +:prop_gbl:`AUTOGEN_TARGETS_FOLDER`: +This global property can be used to group :prop_tgt:`AUTOMOC`, +:prop_tgt:`AUTOUIC` and :prop_tgt:`AUTORCC` targets together in an IDE, +e.g. in MSVS. -The number of parallel ``uic`` processes to start can be modified by -setting :prop_tgt:`AUTOGEN_PARALLEL`. +:variable:`CMAKE_GLOBAL_AUTOGEN_TARGET`: +A global ``autogen`` target, that depends on all :prop_tgt:`AUTOMOC` or +:prop_tgt:`AUTOUIC` generated ``<ORIGIN>_autogen`` targets in the project, +will be generated when this variable is ``ON``. -A global ``autogen`` target that depends on all :prop_tgt:`AUTOUIC` generated -``<ORIGIN>_autogen`` targets in the project can be generated by enabling -:variable:`CMAKE_GLOBAL_AUTOGEN_TARGET`. +:prop_tgt:`AUTOGEN_PARALLEL`: +This target property controls the number of ``moc`` or ``uic`` processes to +start in parallel during builds. See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/IMPORTED_LOCATION.rst b/Help/prop_tgt/IMPORTED_LOCATION.rst index d674f29..a9123cd 100644 --- a/Help/prop_tgt/IMPORTED_LOCATION.rst +++ b/Help/prop_tgt/IMPORTED_LOCATION.rst @@ -15,7 +15,14 @@ is the location of the ``.dll`` part of the library. For ``UNKNOWN`` libraries this is the location of the file to be linked. Ignored for non-imported targets. -Projects may skip ``IMPORTED_LOCATION`` if the configuration-specific -property :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` is set. To get the location -of an imported target read one of the :prop_tgt:`LOCATION` or -``LOCATION_<CONFIG>`` properties. +The ``IMPORTED_LOCATION`` target property may be overridden for a +given configuration ``<CONFIG>`` by the configuration-specific +:prop_tgt:`IMPORTED_LOCATION_<CONFIG>` target property. Furthermore, +the :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` target property may be +used to map between a project's configurations and those of an imported +target. If none of these is set then the name of any other configuration +listed in the :prop_tgt:`IMPORTED_CONFIGURATIONS` target property may be +selected and its :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` value used. + +To get the location of an imported target read one of the :prop_tgt:`LOCATION` +or ``LOCATION_<CONFIG>`` properties. diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst new file mode 100644 index 0000000..e4cc01e --- /dev/null +++ b/Help/release/dev/0-sample-topic.rst @@ -0,0 +1,7 @@ +0-sample-topic +-------------- + +* This is a sample release note for the change in a topic. + Developers should add similar notes for each topic branch + making a noteworthy change. Each document should be named + and titled to match the topic name to avoid merge conflicts. diff --git a/Help/release/dev/CMP0095.rst b/Help/release/dev/CMP0095.rst new file mode 100644 index 0000000..21d0550 --- /dev/null +++ b/Help/release/dev/CMP0095.rst @@ -0,0 +1,5 @@ +CMP0095 +------- + +* ``RPATH`` entries are properly escaped in the intermediary CMake install script. + See policy :policy:`CMP0095`. diff --git a/Help/release/dev/add_test-expand_lists.rst b/Help/release/dev/add_test-expand_lists.rst new file mode 100644 index 0000000..88d26b7 --- /dev/null +++ b/Help/release/dev/add_test-expand_lists.rst @@ -0,0 +1,6 @@ +add_test-expand_lists +--------------------- + +* The command :command:`add_test` learned the option ``COMMAND_EXPAND_LISTS`` + which causes lists in the ``COMMAND`` argument to be expanded, including + lists created by generator expressions. diff --git a/Help/release/dev/get-runtime-dependencies.rst b/Help/release/dev/get-runtime-dependencies.rst new file mode 100644 index 0000000..b9dc6e6 --- /dev/null +++ b/Help/release/dev/get-runtime-dependencies.rst @@ -0,0 +1,9 @@ +get-runtime-dependencies +------------------------ + +* The :command:`file` command learned a new sub-command, + ``GET_RUNTIME_DEPENDENCIES``, which allows you to recursively get the list of + libraries linked by an executable or library. This sub-command is intended as + a replacement for :module:`GetPrerequisites`. +* The :module:`GetPrerequisites` module has been deprecated, as it has been + superceded by :command:`file(GET_RUNTIME_DEPENDENCIES)`. diff --git a/Help/release/dev/global-controls-over-find-locations.rst b/Help/release/dev/global-controls-over-find-locations.rst new file mode 100644 index 0000000..d915fbb --- /dev/null +++ b/Help/release/dev/global-controls-over-find-locations.rst @@ -0,0 +1,11 @@ +global-controls-over-find-locations +----------------------------------- + +* The :command:`find_file`, :command:`find_library`, :command:`find_path`, + and :command:`find_program` commands have learned to check the following + variables to control searching + * :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH` - Controls the searching the cmake-specific environment variables. + * :variable:`CMAKE_FIND_USE_CMAKE_PATH` - Controls the searching the cmake-specific cache variables. + * :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH` - Controls the searching cmake platform specific variables. + * :variable:`CMAKE_FIND_USE_PACAKGE_ROOT_PATH` - Controls the searching of :variable:`<PackageName>_ROOT` variables. + * :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` - Controls the searching the standard system environment variables. diff --git a/Help/release/index.rst b/Help/release/index.rst index 35a47aa..62032e6 100644 --- a/Help/release/index.rst +++ b/Help/release/index.rst @@ -7,6 +7,8 @@ CMake Release Notes This file should include the adjacent "dev.txt" file in development versions but not in release versions. +.. include:: dev.txt + Releases ======== diff --git a/Help/variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH.rst b/Help/variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH.rst new file mode 100644 index 0000000..3db510f --- /dev/null +++ b/Help/variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH.rst @@ -0,0 +1,11 @@ +CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH +------------------------------------- + +Controls the searching the cmake-specific environment variables by the :command:`find_program`, +:command:`find_library`, :command:`find_file`, :command:`find_package`, and :command:`find_path`. +This is useful in cross-compiling environments. + +By default this this is set to ``TRUE``. + +See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`, :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`, +:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`, and :variable:`CMAKE_FIND_USE_PACAKGE_ROOT_PATH` variables. diff --git a/Help/variable/CMAKE_FIND_USE_CMAKE_PATH.rst b/Help/variable/CMAKE_FIND_USE_CMAKE_PATH.rst new file mode 100644 index 0000000..86a6186 --- /dev/null +++ b/Help/variable/CMAKE_FIND_USE_CMAKE_PATH.rst @@ -0,0 +1,11 @@ +CMAKE_FIND_USE_CMAKE_PATH +------------------------- + +Controls the searching the cmake-specific cache variables by the :command:`find_program`, +:command:`find_library`, :command:`find_file`, :command:`find_package`, and :command:`find_path`. +This is useful in cross-compiling environments. + +By default this this is set to ``TRUE``. + +See also the :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`, :variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`, +:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`, and :variable:`CMAKE_FIND_USE_PACAKGE_ROOT_PATH` variables. diff --git a/Help/variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH.rst b/Help/variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH.rst new file mode 100644 index 0000000..b4171c0 --- /dev/null +++ b/Help/variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH.rst @@ -0,0 +1,11 @@ +CMAKE_FIND_USE_CMAKE_SYSTEM_PATH +-------------------------------- + +Controls the searching cmake platform specific variables by the :command:`find_program`, +:command:`find_library`, :command:`find_file`, :command:`find_package`, and :command:`find_path`. +This is useful in cross-compiling environments. + +By default this this is set to ``TRUE``. + +See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`, :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`, +:variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH`, and :variable:`CMAKE_FIND_USE_PACAKGE_ROOT_PATH` variables. diff --git a/Help/variable/CMAKE_FIND_USE_PACAKGE_ROOT_PATH.rst b/Help/variable/CMAKE_FIND_USE_PACAKGE_ROOT_PATH.rst new file mode 100644 index 0000000..ac25ec9 --- /dev/null +++ b/Help/variable/CMAKE_FIND_USE_PACAKGE_ROOT_PATH.rst @@ -0,0 +1,11 @@ +CMAKE_FIND_USE_PACAKGE_ROOT_PATH +-------------------------------- + +Controls the searching of :variable:`<PackageName>_ROOT` variables by the :command:`find_program`, +:command:`find_library`, :command:`find_file`, :command:`find_package`, and :command:`find_path`. +This is useful in cross-compiling environments. + +By default this this is set to ``TRUE``. + +See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`, :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`, +:variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`, and :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` variables. diff --git a/Help/variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH.rst b/Help/variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH.rst new file mode 100644 index 0000000..fd0582f --- /dev/null +++ b/Help/variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH.rst @@ -0,0 +1,11 @@ +CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH +-------------------------------------- + +Controls the searching the standard system environment variables by the :command:`find_program`, +:command:`find_library`, :command:`find_file`, :command:`find_package`, and :command:`find_path`. +This is useful in cross-compiling environments. + +By default this this is set to ``TRUE``. + +See also the :variable:`CMAKE_FIND_USE_CMAKE_PATH`, :variable:`CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH`, +:variable:`CMAKE_FIND_USE_CMAKE_SYSTEM_PATH`, and :variable:`CMAKE_FIND_USE_PACAKGE_ROOT_PATH` variables. diff --git a/Modules/BundleUtilities.cmake b/Modules/BundleUtilities.cmake index f94fc5c..2f3b9e1 100644 --- a/Modules/BundleUtilities.cmake +++ b/Modules/BundleUtilities.cmake @@ -243,11 +243,13 @@ if(DEFINED CMAKE_GENERATOR) endif() endif() +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) # if IN_LIST + # The functions defined in this file depend on the get_prerequisites function # (and possibly others) found in: # -get_filename_component(BundleUtilities_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) -include("${BundleUtilities_cmake_dir}/GetPrerequisites.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/GetPrerequisites.cmake") function(get_bundle_main_executable bundle result_var) @@ -280,7 +282,7 @@ function(get_bundle_main_executable bundle result_var) endif() endforeach() - if(NOT "${bundle_executable}" STREQUAL "") + if(NOT bundle_executable STREQUAL "") if(EXISTS "${bundle}/Contents/MacOS/${bundle_executable}") set(result "${bundle}/Contents/MacOS/${bundle_executable}") else() @@ -600,17 +602,9 @@ function(get_bundle_keys app libs dirs keys_var) set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0 "${main_rpaths}") set(prereqs "") - set(ignoreFile FALSE) get_filename_component(prereq_filename ${lib} NAME) - if(NOT "${CFG_IGNORE_ITEM}" STREQUAL "" ) - foreach(item ${CFG_IGNORE_ITEM}) - if("${item}" STREQUAL "${prereq_filename}") - set(ignoreFile TRUE) - endif() - endforeach() - endif() - if(NOT ignoreFile) + if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM) get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}" "${main_rpaths}") foreach(pr ${prereqs}) set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1 "${main_rpaths}") @@ -627,7 +621,7 @@ function(get_bundle_keys app libs dirs keys_var) foreach(exe ${exes}) # Main executable is scanned first above: # - if(NOT "${exe}" STREQUAL "${executable}") + if(NOT exe STREQUAL executable) # Add the exe itself to the keys: # set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0 "${main_rpaths}") @@ -643,17 +637,9 @@ function(get_bundle_keys app libs dirs keys_var) # Add each prerequisite to the keys: # set(prereqs "") - set(ignoreFile FALSE) get_filename_component(prereq_filename ${exe} NAME) - if(NOT "${CFG_IGNORE_ITEM}" STREQUAL "" ) - foreach(item ${CFG_IGNORE_ITEM}) - if("${item}" STREQUAL "${prereq_filename}") - set(ignoreFile TRUE) - endif() - endforeach() - endif() - if(NOT ignoreFile) + if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM) get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}" "${exe_rpaths}") foreach(pr ${prereqs}) set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1 "${exe_rpaths}") @@ -665,7 +651,7 @@ function(get_bundle_keys app libs dirs keys_var) # preserve library symlink structure foreach(key ${${keys_var}}) - if("${${key}_COPYFLAG}" STREQUAL 1) + if("${${key}_COPYFLAG}" STREQUAL "1") if(IS_SYMLINK "${${key}_RESOLVED_ITEM}") get_filename_component(target "${${key}_RESOLVED_ITEM}" REALPATH) set_bundle_key_values(${keys_var} "${exe}" "${target}" "${exepath}" "${dirs}" 1 "${exe_rpaths}") @@ -682,7 +668,7 @@ function(get_bundle_keys app libs dirs keys_var) get_filename_component(resolved_item_compare "${resolved_item_compare}" NAME) get_filename_component(resolved_embedded_item_compare "${resolved_embedded_item_compare}" NAME) - if(NOT "${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") + if(NOT resolved_item_compare STREQUAL resolved_embedded_item_compare) set(${key}_COPYFLAG "2") set(${key}_RESOLVED_ITEM "${${targetkey}_RESOLVED_EMBEDDED_ITEM}") endif() @@ -716,7 +702,7 @@ function(link_resolved_item_into_bundle resolved_item resolved_embedded_item) set(resolved_embedded_item_compare "${resolved_embedded_item}") endif() - if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") + if(resolved_item_compare STREQUAL resolved_embedded_item_compare) message(STATUS "warning: resolved_item == resolved_embedded_item - not linking...") else() get_filename_component(target_dir "${resolved_embedded_item}" DIRECTORY) @@ -738,7 +724,7 @@ function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item) set(resolved_embedded_item_compare "${resolved_embedded_item}") endif() - if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") + if(resolved_item_compare STREQUAL resolved_embedded_item_compare) message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...") else() #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}") @@ -761,7 +747,7 @@ function(copy_resolved_framework_into_bundle resolved_item resolved_embedded_ite set(resolved_embedded_item_compare "${resolved_embedded_item}") endif() - if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") + if(resolved_item_compare STREQUAL resolved_embedded_item_compare) message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...") else() if(BU_COPY_FULL_FRAMEWORK_CONTENTS) @@ -841,12 +827,12 @@ function(fixup_bundle_item resolved_embedded_item exepath dirs) string(LENGTH "${resolved_embedded_item}" resolved_embedded_item_length) set(path_too_short 0) set(is_embedded 0) - if(${resolved_embedded_item_length} LESS ${exe_dotapp_dir_length}) + if(resolved_embedded_item_length LESS exe_dotapp_dir_length) set(path_too_short 1) endif() if(NOT path_too_short) string(SUBSTRING "${resolved_embedded_item}" 0 ${exe_dotapp_dir_length} item_substring) - if("${exe_dotapp_dir}/" STREQUAL "${item_substring}") + if("${exe_dotapp_dir}/" STREQUAL item_substring) set(is_embedded 1) endif() endif() @@ -1032,18 +1018,9 @@ function(verify_bundle_prerequisites bundle result_var info_var) message(STATUS "executable file ${count}: ${f}") set(prereqs "") - set(ignoreFile FALSE) get_filename_component(prereq_filename ${f} NAME) - if(NOT "${CFG_IGNORE_ITEM}" STREQUAL "" ) - foreach(item ${CFG_IGNORE_ITEM}) - if("${item}" STREQUAL "${prereq_filename}") - set(ignoreFile TRUE) - endif() - endforeach() - endif() - - if(NOT ignoreFile) + if(NOT prereq_filename IN_LIST CFG_IGNORE_ITEM) get_item_rpaths(${f} _main_exe_rpaths) get_prerequisites("${f}" prereqs 1 1 "${exepath}" "${_main_exe_rpaths}") @@ -1063,11 +1040,11 @@ function(verify_bundle_prerequisites bundle result_var info_var) gp_file_type("${f}" "${p}" p_type) if(APPLE) - if(NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system") + if(NOT p_type STREQUAL "embedded" AND NOT p_type STREQUAL "system") set(external_prereqs ${external_prereqs} "${p}") endif() else() - if(NOT "${p_type}" STREQUAL "local" AND NOT "${p_type}" STREQUAL "system") + if(NOT p_type STREQUAL "local" AND NOT p_type STREQUAL "system") set(external_prereqs ${external_prereqs} "${p}") endif() endif() @@ -1142,3 +1119,5 @@ function(verify_app app) message(FATAL_ERROR "error: verify_app failed") endif() endfunction() + +cmake_policy(POP) diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake index 21f18d4..6f99c1f 100644 --- a/Modules/CMakeSwiftInformation.cmake +++ b/Modules/CMakeSwiftInformation.cmake @@ -23,6 +23,8 @@ set(CMAKE_Swift_COMPILE_OPTIONS_TARGET "-target ") set(CMAKE_Swift_COMPILER_ARG1 -frontend) set(CMAKE_Swift_DEFINE_FLAG -D) set(CMAKE_Swift_FRAMEWORK_SEARCH_FLAG "-F ") +set(CMAKE_Swift_LIBRARY_PATH_FLAG "-L ") +set(CMAKE_Swift_LIBRARY_PATH_TERMINATOR "") set(CMAKE_Swift_LINKER_WRAPPER_FLAG "-Xlinker" " ") set(CMAKE_Swift_RESPONSE_FILE_LINK_FLAG @) diff --git a/Modules/Compiler/GNU-CXX-FeatureTests.cmake b/Modules/Compiler/GNU-CXX-FeatureTests.cmake index d18adaf..45c5470 100644 --- a/Modules/Compiler/GNU-CXX-FeatureTests.cmake +++ b/Modules/Compiler/GNU-CXX-FeatureTests.cmake @@ -18,18 +18,18 @@ set(_cmake_feature_test_cxx_attribute_deprecated "${GNU49_CXX14}") set(_cmake_feature_test_cxx_decltype_auto "${GNU49_CXX14}") set(_cmake_feature_test_cxx_digit_separators "${GNU49_CXX14}") set(_cmake_feature_test_cxx_generic_lambdas "${GNU49_CXX14}") -set(_cmake_feature_test_cxx_lambda_init_captures "${GNU49_CXX14}") # GNU 4.3 supports binary literals as an extension, but may warn about # use of extensions prior to GNU 4.9 # http://stackoverflow.com/questions/16334024/difference-between-gcc-binary-literals-and-c14-ones set(_cmake_feature_test_cxx_binary_literals "${GNU49_CXX14}") -# The feature below is documented as available in GNU 4.8 (by implementing an +# The features below are documented as available in GNU 4.8 (by implementing an # earlier draft of the standard paper), but that version of the compiler # does not set __cplusplus to a value greater than 201103L until GNU 4.9: # http://gcc.gnu.org/onlinedocs/gcc-4.8.2/cpp/Standard-Predefined-Macros.html#Standard-Predefined-Macros # http://gcc.gnu.org/onlinedocs/gcc-4.9.0/cpp/Standard-Predefined-Macros.html#Standard-Predefined-Macros # So, CMake only reports availability for it with GNU 4.9 or later. set(_cmake_feature_test_cxx_return_type_deduction "${GNU49_CXX14}") +set(_cmake_feature_test_cxx_lambda_init_captures "${GNU49_CXX14}") # Introduced in GCC 4.8.1 set(GNU481_CXX11 "((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40801) && __cplusplus >= 201103L") diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake index 3315505..b6859aa 100644 --- a/Modules/FindCUDA.cmake +++ b/Modules/FindCUDA.cmake @@ -375,6 +375,11 @@ The script defines the following variables:: CUDA_nvcuvid_LIBRARY -- CUDA Video Decoder library. Only available for CUDA version 3.2+. Windows only. + CUDA_nvToolsExt_LIBRARY + -- NVIDA CUDA Tools Extension library. + Available for CUDA version 5+. + CUDA_OpenCL_LIBRARY -- NVIDA CUDA OpenCL library. + Available for CUDA version 5+. #]=======================================================================] @@ -642,6 +647,8 @@ macro(cuda_unset_include_and_libraries) unset(CUDA_npps_LIBRARY CACHE) unset(CUDA_nvcuvenc_LIBRARY CACHE) unset(CUDA_nvcuvid_LIBRARY CACHE) + unset(CUDA_nvToolsExt_LIBRARY CACHE) + unset(CUDA_OpenCL_LIBRARY CACHE) unset(CUDA_GPU_DETECT_OUTPUT CACHE) endmacro() @@ -973,6 +980,11 @@ if(CUDA_VERSION VERSION_GREATER "5.0" AND CUDA_VERSION VERSION_LESS "9.2") find_cuda_helper_libs(cublas_device) endif() +if(NOT CUDA_VERSION VERSION_LESS "5.0") + find_cuda_helper_libs(nvToolsExt) + find_cuda_helper_libs(OpenCL) +endif() + if(NOT CUDA_VERSION VERSION_LESS "9.0") # In CUDA 9.0 NPP was nppi was removed find_cuda_helper_libs(nppc) diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake index c9c3cce..844d36d 100644 --- a/Modules/FindLAPACK.cmake +++ b/Modules/FindLAPACK.cmake @@ -173,11 +173,15 @@ if(_libraries_work) #message("DEBUG: ${LIBRARIES} = ${${LIBRARIES}}") endif() -if(_libraries_work) - set(${LIBRARIES} ${${LIBRARIES}} ${_blas} ${_threads}) -else() - set(${LIBRARIES} FALSE) -endif() + if(_libraries_work) + if("${_list}" STREQUAL "") + set(${LIBRARIES} "${LIBRARIES}-PLACEHOLDER-FOR-EMPTY-LIBRARIES") + else() + set(${LIBRARIES} ${${LIBRARIES}} ${_blas} ${_threads}) + endif() + else() + set(${LIBRARIES} FALSE) + endif() endmacro() @@ -206,6 +210,7 @@ if(BLAS_FOUND) #intel lapack if (BLA_VENDOR MATCHES "Intel" OR BLA_VENDOR STREQUAL "All") + if(NOT LAPACK_LIBRARIES) if (NOT WIN32) set(LAPACK_mkl_LM "-lm") set(LAPACK_mkl_LDL "-ldl") @@ -280,6 +285,7 @@ if (BLA_VENDOR MATCHES "Intel" OR BLA_VENDOR STREQUAL "All") unset(LAPACK_mkl_LM) unset(LAPACK_mkl_LDL) endif () + endif() endif() if (BLA_VENDOR STREQUAL "Goto" OR BLA_VENDOR STREQUAL "All") @@ -426,5 +432,11 @@ else() endif() endif() +# On compilers that implicitly link LAPACK (such as ftn, cc, and CC on Cray HPC machines) +# we used a placeholder for empty LAPACK_LIBRARIES to get through our logic above. +if (LAPACK_LIBRARIES STREQUAL "LAPACK_LIBRARIES-PLACEHOLDER-FOR-EMPTY-LIBRARIES") + set(LAPACK_LIBRARIES "") +endif() + cmake_pop_check_state() set(CMAKE_FIND_LIBRARY_SUFFIXES ${_lapack_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake index 5be4676..2beea00 100644 --- a/Modules/GetPrerequisites.cmake +++ b/Modules/GetPrerequisites.cmake @@ -5,6 +5,10 @@ GetPrerequisites ---------------- +.. deprecated:: 3.16 + + Use :command:`file(GET_RUNTIME_DEPENDENCIES)` instead. + Functions to analyze and list executable file prerequisites. This module provides functions to list the .dll, .dylib or .so files @@ -706,7 +710,9 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths}) if(gp_dumpbin) set(gp_tool "dumpbin") - else() # Try harder. Maybe we're on MinGW + elseif(CMAKE_OBJDUMP) # Try harder. Maybe we're on MinGW + set(gp_tool "${CMAKE_OBJDUMP}") + else() set(gp_tool "objdump") endif() endif() diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index 89dc6f0..7791822 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake @@ -45,7 +45,7 @@ function(extract_so_info shared_object libname version) ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(result EQUAL 0) - string(REGEX MATCH "\\(SONAME\\)[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}") + string(REGEX MATCH "\\(?SONAME\\)?[^\n]*\\[([^\n]+)\\.so\\.([^\n]*)\\]" soname "${output}") set(${libname} "${CMAKE_MATCH_1}" PARENT_SCOPE) set(${version} "${CMAKE_MATCH_2}" PARENT_SCOPE) else() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 695e075..0316532 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -146,6 +146,28 @@ set(SRCS cmArgumentParser.cxx cmArgumentParser.h cmBase32.cxx + cmBinUtilsLinker.cxx + cmBinUtilsLinker.h + cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx + cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h + cmBinUtilsLinuxELFLinker.cxx + cmBinUtilsLinuxELFLinker.h + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h + cmBinUtilsMacOSMachOLinker.cxx + cmBinUtilsMacOSMachOLinker.h + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h + cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx + cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h + cmBinUtilsWindowsPELinker.cxx + cmBinUtilsWindowsPELinker.h + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h cmCacheManager.cxx cmCacheManager.h cmCLocaleEnvironmentScope.h @@ -295,6 +317,10 @@ set(SRCS cmInstallTargetGenerator.cxx cmInstallDirectoryGenerator.h cmInstallDirectoryGenerator.cxx + cmLDConfigLDConfigTool.cxx + cmLDConfigLDConfigTool.h + cmLDConfigTool.cxx + cmLDConfigTool.h cmLinkedTree.h cmLinkItem.cxx cmLinkItem.h @@ -338,7 +364,6 @@ set(SRCS cmProcessOutput.h cmProcessTools.cxx cmProcessTools.h - cmProperty.cxx cmProperty.h cmPropertyDefinition.cxx cmPropertyDefinition.h @@ -360,6 +385,8 @@ set(SRCS cmQtAutoRcc.h cmRST.cxx cmRST.h + cmRuntimeDependencyArchive.cxx + cmRuntimeDependencyArchive.h cmScriptGenerator.h cmScriptGenerator.cxx cmSourceFile.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 3010542..78f236f 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 15) -set(CMake_VERSION_PATCH 0) -set(CMake_VERSION_RC 2) +set(CMake_VERSION_PATCH 20190624) +#set(CMake_VERSION_RC 1) diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 0be3c85..6eb38bd 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -95,30 +95,29 @@ bool cmAddCustomCommandCommand::InitialPass( MAKE_STATIC_KEYWORD(VERBATIM); MAKE_STATIC_KEYWORD(WORKING_DIRECTORY); #undef MAKE_STATIC_KEYWORD - static std::unordered_set<std::string> keywords; - if (keywords.empty()) { - keywords.insert(keyAPPEND); - keywords.insert(keyARGS); - keywords.insert(keyBYPRODUCTS); - keywords.insert(keyCOMMAND); - keywords.insert(keyCOMMAND_EXPAND_LISTS); - keywords.insert(keyCOMMENT); - keywords.insert(keyDEPENDS); - keywords.insert(keyDEPFILE); - keywords.insert(keyIMPLICIT_DEPENDS); - keywords.insert(keyJOB_POOL); - keywords.insert(keyMAIN_DEPENDENCY); - keywords.insert(keyOUTPUT); - keywords.insert(keyOUTPUTS); - keywords.insert(keyPOST_BUILD); - keywords.insert(keyPRE_BUILD); - keywords.insert(keyPRE_LINK); - keywords.insert(keySOURCE); - keywords.insert(keyTARGET); - keywords.insert(keyUSES_TERMINAL); - keywords.insert(keyVERBATIM); - keywords.insert(keyWORKING_DIRECTORY); - } + static std::unordered_set<std::string> const keywords{ + keyAPPEND, + keyARGS, + keyBYPRODUCTS, + keyCOMMAND, + keyCOMMAND_EXPAND_LISTS, + keyCOMMENT, + keyDEPENDS, + keyDEPFILE, + keyIMPLICIT_DEPENDS, + keyJOB_POOL, + keyMAIN_DEPENDENCY, + keyOUTPUT, + keyOUTPUTS, + keyPOST_BUILD, + keyPRE_BUILD, + keyPRE_LINK, + keySOURCE, + keyTARGET, + keyUSES_TERMINAL, + keyVERBATIM, + keyWORKING_DIRECTORY + }; for (std::string const& copy : args) { if (keywords.count(copy)) { diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx index bf28702..b0c462b 100644 --- a/Source/cmAddTestCommand.cxx +++ b/Source/cmAddTestCommand.cxx @@ -58,6 +58,7 @@ bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args) std::vector<std::string> configurations; std::string working_directory; std::vector<std::string> command; + bool command_expand_lists = false; // Read the arguments. enum Doing @@ -88,6 +89,13 @@ bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args) return false; } doing = DoingWorkingDirectory; + } else if (args[i] == "COMMAND_EXPAND_LISTS") { + if (command_expand_lists) { + this->SetError(" may be given at most one COMMAND_EXPAND_LISTS."); + return false; + } + command_expand_lists = true; + doing = DoingNone; } else if (doing == DoingName) { name = args[i]; doing = DoingNone; @@ -134,6 +142,7 @@ bool cmAddTestCommand::HandleNameMode(std::vector<std::string> const& args) if (!working_directory.empty()) { test->SetProperty("WORKING_DIRECTORY", working_directory.c_str()); } + test->SetCommandExpandLists(command_expand_lists); this->Makefile->AddTestGenerator(new cmTestGenerator(test, configurations)); return true; diff --git a/Source/cmBinUtilsLinker.cxx b/Source/cmBinUtilsLinker.cxx new file mode 100644 index 0000000..3dac85c --- /dev/null +++ b/Source/cmBinUtilsLinker.cxx @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinker.h" +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsLinker::cmBinUtilsLinker(cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsLinker::SetError(const std::string& e) +{ + this->Archive->SetError(e); +} diff --git a/Source/cmBinUtilsLinker.h b/Source/cmBinUtilsLinker.h new file mode 100644 index 0000000..29853a5 --- /dev/null +++ b/Source/cmBinUtilsLinker.h @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinker_h +#define cmBinUtilsLinker_h + +#include "cmStateTypes.h" + +#include <string> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinker +{ +public: + cmBinUtilsLinker(cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsLinker() = default; + + virtual bool Prepare() { return true; } + + virtual bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& e); +}; + +#endif // cmBinUtilsLinker_h diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..40de592 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx @@ -0,0 +1,18 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsLinuxELFGetRuntimeDependenciesTool:: + cmBinUtilsLinuxELFGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsLinuxELFGetRuntimeDependenciesTool::SetError( + const std::string& error) +{ + this->Archive->SetError(error); +} diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..d514e7f --- /dev/null +++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h @@ -0,0 +1,30 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h +#define cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinuxELFGetRuntimeDependenciesTool +{ +public: + cmBinUtilsLinuxELFGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsLinuxELFGetRuntimeDependenciesTool() = default; + + virtual bool GetFileInfo(std::string const& file, + std::vector<std::string>& needed, + std::vector<std::string>& rpaths, + std::vector<std::string>& runpaths) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& e); +}; + +#endif // cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx new file mode 100644 index 0000000..4fb15f2 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFLinker.cxx @@ -0,0 +1,177 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinuxELFLinker.h" +#include "cmAlgorithms.h" +#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h" +#include "cmLDConfigLDConfigTool.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" + +#include <cmsys/RegularExpression.hxx> + +#include <memory> +#include <sstream> + +static std::string ReplaceOrigin(const std::string& rpath, + const std::string& origin) +{ + static const cmsys::RegularExpression originRegex( + "(\\$ORIGIN)([^a-zA-Z0-9_]|$)"); + static const cmsys::RegularExpression originCurlyRegex("\\${ORIGIN}"); + + cmsys::RegularExpressionMatch match; + if (originRegex.find(rpath.c_str(), match)) { + std::string begin = rpath.substr(0, match.start(1)); + std::string end = rpath.substr(match.end(1)); + return begin + origin + end; + } + if (originCurlyRegex.find(rpath.c_str(), match)) { + std::string begin = rpath.substr(0, match.start()); + std::string end = rpath.substr(match.end()); + return begin + origin + end; + } + return rpath; +} + +cmBinUtilsLinuxELFLinker::cmBinUtilsLinuxELFLinker( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinker(archive) +{ +} + +bool cmBinUtilsLinuxELFLinker::Prepare() +{ + std::string tool = this->Archive->GetGetRuntimeDependenciesTool(); + if (tool.empty()) { + tool = "objdump"; + } + if (tool == "objdump") { + this->Tool = + cm::make_unique<cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool>( + this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool; + this->SetError(e.str()); + return false; + } + + std::string ldConfigTool = + this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_TOOL"); + if (ldConfigTool.empty()) { + ldConfigTool = "ldconfig"; + } + if (ldConfigTool == "ldconfig") { + this->LDConfigTool = + cm::make_unique<cmLDConfigLDConfigTool>(this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool; + this->SetError(e.str()); + return false; + } + + return true; +} + +bool cmBinUtilsLinuxELFLinker::ScanDependencies( + std::string const& file, cmStateEnums::TargetType /* unused */) +{ + std::vector<std::string> parentRpaths; + return this->ScanDependencies(file, parentRpaths); +} + +bool cmBinUtilsLinuxELFLinker::ScanDependencies( + std::string const& file, std::vector<std::string> const& parentRpaths) +{ + std::string origin = cmSystemTools::GetFilenamePath(file); + std::vector<std::string> needed; + std::vector<std::string> rpaths; + std::vector<std::string> runpaths; + if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) { + return false; + } + for (auto& runpath : runpaths) { + runpath = ReplaceOrigin(runpath, origin); + } + for (auto& rpath : rpaths) { + rpath = ReplaceOrigin(rpath, origin); + } + + std::vector<std::string> searchPaths; + if (!runpaths.empty()) { + searchPaths = runpaths; + } else { + searchPaths = rpaths; + searchPaths.insert(searchPaths.end(), parentRpaths.begin(), + parentRpaths.end()); + } + + std::vector<std::string> ldConfigPaths; + if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) { + return false; + } + searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(), + ldConfigPaths.end()); + + for (auto const& dep : needed) { + if (!this->Archive->IsPreExcluded(dep)) { + std::string path; + bool resolved = false; + if (dep.find('/') != std::string::npos) { + this->SetError("Paths to dependencies are not supported"); + return false; + } + if (!this->ResolveDependency(dep, searchPaths, path, resolved)) { + return false; + } + if (resolved) { + if (!this->Archive->IsPostExcluded(path)) { + bool unique; + this->Archive->AddResolvedPath(dep, path, unique); + if (unique && !this->ScanDependencies(path, rpaths)) { + return false; + } + } + } else { + this->Archive->AddUnresolvedPath(dep); + } + } + } + + return true; +} + +bool cmBinUtilsLinuxELFLinker::ResolveDependency( + std::string const& name, std::vector<std::string> const& searchPaths, + std::string& path, bool& resolved) +{ + for (auto const& searchPath : searchPaths) { + path = searchPath + '/' + name; + if (cmSystemTools::PathExists(path)) { + resolved = true; + return true; + } + } + + for (auto const& searchPath : this->Archive->GetSearchDirectories()) { + path = searchPath + '/' + name; + if (cmSystemTools::PathExists(path)) { + std::ostringstream warning; + warning << "Dependency " << name << " found in search directory:\n " + << searchPath + << "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for " + << "more information."; + this->Archive->GetMakefile()->IssueMessage(MessageType::WARNING, + warning.str()); + resolved = true; + return true; + } + } + + resolved = false; + return true; +} diff --git a/Source/cmBinUtilsLinuxELFLinker.h b/Source/cmBinUtilsLinuxELFLinker.h new file mode 100644 index 0000000..348edc4 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFLinker.h @@ -0,0 +1,44 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinuxELFLinker_h +#define cmBinUtilsLinuxELFLinker_h + +#include "cmBinUtilsLinker.h" +#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h" +#include "cmLDConfigTool.h" +#include "cmStateTypes.h" + +#include <memory> // IWYU pragma: keep +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinuxELFLinker : public cmBinUtilsLinker +{ +public: + cmBinUtilsLinuxELFLinker(cmRuntimeDependencyArchive* archive); + + bool Prepare() override; + + bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) override; + +private: + std::unique_ptr<cmBinUtilsLinuxELFGetRuntimeDependenciesTool> Tool; + std::unique_ptr<cmLDConfigTool> LDConfigTool; + bool HaveLDConfigPaths = false; + std::vector<std::string> LDConfigPaths; + + bool ScanDependencies(std::string const& file, + std::vector<std::string> const& parentRpaths); + + bool ResolveDependency(std::string const& name, + std::vector<std::string> const& searchPaths, + std::string& path, bool& resolved); + + bool GetLDConfigPaths(); +}; + +#endif // cmBinUtilsLinuxELFLinker_h diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..3bf7bf8 --- /dev/null +++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx @@ -0,0 +1,84 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool:: + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinuxELFGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::GetFileInfo( + std::string const& file, std::vector<std::string>& needed, + std::vector<std::string>& rpaths, std::vector<std::string>& runpaths) +{ + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) { + this->SetError("Could not find objdump"); + return false; + } + command.emplace_back("-p"); + command.push_back(file); + builder.AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression neededRegex("^ *NEEDED *([^\n]*)$"); + static const cmsys::RegularExpression rpathRegex("^ *RPATH *([^\n]*)$"); + static const cmsys::RegularExpression runpathRegex("^ *RUNPATH *([^\n]*)$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (neededRegex.find(line.c_str(), match)) { + needed.push_back(match.match(1)); + } else if (rpathRegex.find(line.c_str(), match)) { + std::vector<std::string> rpathSplit = + cmSystemTools::SplitString(match.match(1), ':'); + rpaths.reserve(rpaths.size() + rpathSplit.size()); + for (auto const& rpath : rpathSplit) { + rpaths.push_back(rpath); + } + } else if (runpathRegex.find(line.c_str(), match)) { + std::vector<std::string> runpathSplit = + cmSystemTools::SplitString(match.match(1), ':'); + runpaths.reserve(runpaths.size() + runpathSplit.size()); + for (auto const& runpath : runpathSplit) { + runpaths.push_back(runpath); + } + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run objdump on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..286337f --- /dev/null +++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h @@ -0,0 +1,26 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h +#define cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h + +#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool + : public cmBinUtilsLinuxELFGetRuntimeDependenciesTool +{ +public: + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(std::string const& file, std::vector<std::string>& needed, + std::vector<std::string>& rpaths, + std::vector<std::string>& runpaths) override; +}; + +#endif // cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..a296a47 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx @@ -0,0 +1,19 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h" + +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsMacOSMachOGetRuntimeDependenciesTool:: + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::SetError( + const std::string& error) +{ + this->Archive->SetError(error); +} diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..dbb2882 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h @@ -0,0 +1,29 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h +#define cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsMacOSMachOGetRuntimeDependenciesTool +{ +public: + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsMacOSMachOGetRuntimeDependenciesTool() = default; + + virtual bool GetFileInfo(std::string const& file, + std::vector<std::string>& libs, + std::vector<std::string>& rpaths) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& error); +}; + +#endif // cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx new file mode 100644 index 0000000..e219847 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOLinker.cxx @@ -0,0 +1,228 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsMacOSMachOLinker.h" + +#include "cmAlgorithms.h" +#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" + +#include <sstream> +#include <string> +#include <vector> + +cmBinUtilsMacOSMachOLinker::cmBinUtilsMacOSMachOLinker( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinker(archive) +{ +} + +bool cmBinUtilsMacOSMachOLinker::Prepare() +{ + std::string tool = this->Archive->GetGetRuntimeDependenciesTool(); + if (tool.empty()) { + tool = "otool"; + } + if (tool == "otool") { + this->Tool = + cm::make_unique<cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool>( + this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool; + this->SetError(e.str()); + return false; + } + + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ScanDependencies( + std::string const& file, cmStateEnums::TargetType type) +{ + std::string executableFile; + if (type == cmStateEnums::EXECUTABLE) { + executableFile = file; + } else { + executableFile = this->Archive->GetBundleExecutable(); + } + std::string executablePath; + if (!executableFile.empty()) { + executablePath = cmSystemTools::GetFilenamePath(executableFile); + } + return this->ScanDependencies(file, executablePath); +} + +bool cmBinUtilsMacOSMachOLinker::ScanDependencies( + std::string const& file, std::string const& executablePath) +{ + std::vector<std::string> libs, rpaths; + if (!this->Tool->GetFileInfo(file, libs, rpaths)) { + return false; + } + + std::string loaderPath = cmSystemTools::GetFilenamePath(file); + return this->GetFileDependencies(libs, executablePath, loaderPath, rpaths); +} + +bool cmBinUtilsMacOSMachOLinker::GetFileDependencies( + std::vector<std::string> const& names, std::string const& executablePath, + std::string const& loaderPath, std::vector<std::string> const& rpaths) +{ + for (std::string const& name : names) { + if (!this->Archive->IsPreExcluded(name)) { + std::string path; + bool resolved; + if (!this->ResolveDependency(name, executablePath, loaderPath, rpaths, + path, resolved)) { + return false; + } + if (resolved) { + if (!this->Archive->IsPostExcluded(path)) { + auto filename = cmSystemTools::GetFilenameName(path); + bool unique; + this->Archive->AddResolvedPath(filename, path, unique); + if (unique && !this->ScanDependencies(path, executablePath)) { + return false; + } + } + } else { + this->Archive->AddUnresolvedPath(name); + } + } + } + + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveDependency( + std::string const& name, std::string const& executablePath, + std::string const& loaderPath, std::vector<std::string> const& rpaths, + std::string& path, bool& resolved) +{ + resolved = false; + if (cmHasLiteralPrefix(name, "@rpath/")) { + if (!this->ResolveRPathDependency(name, executablePath, loaderPath, rpaths, + path, resolved)) { + return false; + } + } else if (cmHasLiteralPrefix(name, "@loader_path/")) { + if (!this->ResolveLoaderPathDependency(name, loaderPath, path, resolved)) { + return false; + } + } else if (cmHasLiteralPrefix(name, "@executable_path/")) { + if (!this->ResolveExecutablePathDependency(name, executablePath, path, + resolved)) { + return false; + } + } else { + resolved = true; + path = name; + } + + if (resolved && !cmSystemTools::FileIsFullPath(path)) { + this->SetError("Resolved path is not absolute"); + return false; + } + + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveExecutablePathDependency( + std::string const& name, std::string const& executablePath, + std::string& path, bool& resolved) +{ + if (executablePath.empty()) { + resolved = false; + return true; + } + + // 16 is == "@executable_path".length() + path = name; + path.replace(0, 16, executablePath); + + if (!cmSystemTools::PathExists(path)) { + resolved = false; + return true; + } + + resolved = true; + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveLoaderPathDependency( + std::string const& name, std::string const& loaderPath, std::string& path, + bool& resolved) +{ + if (loaderPath.empty()) { + resolved = false; + return true; + } + + // 12 is "@loader_path".length(); + path = name; + path.replace(0, 12, loaderPath); + + if (!cmSystemTools::PathExists(path)) { + resolved = false; + return true; + } + + resolved = true; + return true; +} + +bool cmBinUtilsMacOSMachOLinker::ResolveRPathDependency( + std::string const& name, std::string const& executablePath, + std::string const& loaderPath, std::vector<std::string> const& rpaths, + std::string& path, bool& resolved) +{ + for (std::string const& rpath : rpaths) { + std::string searchFile = name; + searchFile.replace(0, 6, rpath); + if (cmHasLiteralPrefix(searchFile, "@loader_path/")) { + if (!this->ResolveLoaderPathDependency(searchFile, loaderPath, path, + resolved)) { + return false; + } + if (resolved) { + return true; + } + } else if (cmHasLiteralPrefix(searchFile, "@executable_path/")) { + if (!this->ResolveExecutablePathDependency(searchFile, executablePath, + path, resolved)) { + return false; + } + if (resolved) { + return true; + } + } else if (cmSystemTools::PathExists(searchFile)) { + /* + * paraphrasing @ben.boeckel: + * if /b/libB.dylib is supposed to be used, + * /a/libbB.dylib will be found first if it exists. CMake tries to + * sort rpath directories to avoid this, but sometimes there is no + * right answer. + * + * I believe it is possible to resolve this using otools -l + * then checking the LC_LOAD_DYLIB command whose name is + * equal to the value of search_file, UNLESS the build + * specifically sets the RPath to paths that will match + * duplicate libs; at this point can we just point to + * user error, or is there a reason why the advantages + * to this scenario outweigh its disadvantages? + * + * Also priority seems to be the order as passed in when compiled + * so as long as this method's resolution guarantees priority + * in that manner further checking should not be necessary? + */ + path = searchFile; + resolved = true; + return true; + } + } + + resolved = false; + return true; +} diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h new file mode 100644 index 0000000..0350d1e --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOLinker.h @@ -0,0 +1,59 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsMacOSMachOLinker_h +#define cmBinUtilsMacOSMachOLinker_h + +#include "cmBinUtilsLinker.h" +#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h" +#include "cmStateTypes.h" + +#include <memory> // IWYU pragma: keep +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsMacOSMachOLinker : public cmBinUtilsLinker +{ +public: + cmBinUtilsMacOSMachOLinker(cmRuntimeDependencyArchive* archive); + + bool Prepare() override; + + bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) override; + +private: + std::unique_ptr<cmBinUtilsMacOSMachOGetRuntimeDependenciesTool> Tool; + + bool ScanDependencies(std::string const& file, + std::string const& executablePath); + + bool GetFileDependencies(std::vector<std::string> const& names, + std::string const& executablePath, + std::string const& loaderPath, + std::vector<std::string> const& rpaths); + + bool ResolveDependency(std::string const& name, + std::string const& executablePath, + std::string const& loaderPath, + std::vector<std::string> const& rpaths, + std::string& path, bool& resolved); + + bool ResolveExecutablePathDependency(std::string const& name, + std::string const& executablePath, + std::string& path, bool& resolved); + + bool ResolveLoaderPathDependency(std::string const& name, + std::string const& loaderPath, + std::string& path, bool& resolved); + + bool ResolveRPathDependency(std::string const& name, + std::string const& executablePath, + std::string const& loaderPath, + std::vector<std::string> const& rpaths, + std::string& path, bool& resolved); +}; + +#endif // cmBinUtilsMacOSMachOLinker_h diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..bab2382 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx @@ -0,0 +1,100 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h" + +#include "cmRuntimeDependencyArchive.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool:: + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo( + std::string const& file, std::vector<std::string>& libs, + std::vector<std::string>& rpaths) +{ + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("otool", command)) { + this->SetError("Could not find otool"); + return false; + } + command.emplace_back("-l"); + command.emplace_back(file); + + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start otool process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression rpathRegex("^ *cmd LC_RPATH$"); + static const cmsys::RegularExpression loadDylibRegex( + "^ *cmd LC_LOAD_DYLIB$"); + static const cmsys::RegularExpression pathRegex( + "^ *path (.*) \\(offset [0-9]+\\)$"); + static const cmsys::RegularExpression nameRegex( + "^ *name (.*) \\(offset [0-9]+\\)$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch cmdMatch; + if (rpathRegex.find(line.c_str(), cmdMatch)) { + if (!std::getline(*process.OutputStream(), line) || + !std::getline(*process.OutputStream(), line)) { + this->SetError("Invalid output from otool"); + return false; + } + + cmsys::RegularExpressionMatch pathMatch; + if (pathRegex.find(line.c_str(), pathMatch)) { + rpaths.push_back(pathMatch.match(1)); + } else { + this->SetError("Invalid output from otool"); + return false; + } + } else if (loadDylibRegex.find(line.c_str(), cmdMatch)) { + if (!std::getline(*process.OutputStream(), line) || + !std::getline(*process.OutputStream(), line)) { + this->SetError("Invalid output from otool"); + return false; + } + + cmsys::RegularExpressionMatch nameMatch; + if (nameRegex.find(line.c_str(), nameMatch)) { + libs.push_back(nameMatch.match(1)); + } else { + this->SetError("Invalid output from otool"); + return false; + } + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on otool process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run otool on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..12bcbc1 --- /dev/null +++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h +#define cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h + +#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool + : public cmBinUtilsMacOSMachOGetRuntimeDependenciesTool +{ +public: + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(std::string const& file, std::vector<std::string>& libs, + std::vector<std::string>& rpaths) override; +}; + +#endif // cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..2b35e30 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx @@ -0,0 +1,67 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool:: + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::GetFileInfo( + const std::string& file, std::vector<std::string>& needed) +{ + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) { + this->SetError("Could not find dumpbin"); + return false; + } + command.emplace_back("/dependents"); + command.push_back(file); + builder.AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start dumpbin process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression regex( + "^ ([^\n]*\\.[Dd][Ll][Ll])\r$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (regex.find(line.c_str(), match)) { + needed.push_back(match.match(1)); + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on dumpbin process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run dumpbin on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..4c17f8d --- /dev/null +++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h +#define cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h + +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool + : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool +{ +public: + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(const std::string& file, + std::vector<std::string>& needed) override; +}; + +#endif // cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..f5a4431 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx @@ -0,0 +1,18 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" + +cmBinUtilsWindowsPEGetRuntimeDependenciesTool:: + cmBinUtilsWindowsPEGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} + +void cmBinUtilsWindowsPEGetRuntimeDependenciesTool::SetError( + const std::string& error) +{ + this->Archive->SetError(error); +} diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..e9e402b --- /dev/null +++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h @@ -0,0 +1,28 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h +#define cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPEGetRuntimeDependenciesTool +{ +public: + cmBinUtilsWindowsPEGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + virtual ~cmBinUtilsWindowsPEGetRuntimeDependenciesTool() = default; + + virtual bool GetFileInfo(const std::string& file, + std::vector<std::string>& needed) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; + + void SetError(const std::string& error); +}; + +#endif // cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPELinker.cxx b/Source/cmBinUtilsWindowsPELinker.cxx new file mode 100644 index 0000000..796e9ed --- /dev/null +++ b/Source/cmBinUtilsWindowsPELinker.cxx @@ -0,0 +1,121 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPELinker.h" +#include "cmAlgorithms.h" +#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h" +#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" + +#include <memory> +#include <sstream> +#include <vector> + +#ifdef _WIN32 +# include <windows.h> +#endif + +cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsLinker(archive) +{ +} + +bool cmBinUtilsWindowsPELinker::Prepare() +{ + std::string tool = this->Archive->GetGetRuntimeDependenciesTool(); + if (tool.empty()) { + std::vector<std::string> command; + if (this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) { + tool = "dumpbin"; + } else { + tool = "objdump"; + } + } + if (tool == "dumpbin") { + this->Tool = + cm::make_unique<cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool>( + this->Archive); + } else if (tool == "objdump") { + this->Tool = + cm::make_unique<cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool>( + this->Archive); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool; + this->SetError(e.str()); + return false; + } + + return true; +} + +bool cmBinUtilsWindowsPELinker::ScanDependencies( + std::string const& file, cmStateEnums::TargetType /* unused */) +{ + std::vector<std::string> needed; + if (!this->Tool->GetFileInfo(file, needed)) { + return false; + } + for (auto& n : needed) { + n = cmSystemTools::LowerCase(n); + } + std::string origin = cmSystemTools::GetFilenamePath(file); + + for (auto const& lib : needed) { + if (!this->Archive->IsPreExcluded(lib)) { + std::string path; + bool resolved = false; + if (!this->ResolveDependency(lib, origin, path, resolved)) { + return false; + } + if (resolved) { + if (!this->Archive->IsPostExcluded(path)) { + bool unique; + this->Archive->AddResolvedPath(lib, path, unique); + if (unique && + !this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) { + return false; + } + } + } else { + this->Archive->AddUnresolvedPath(lib); + } + } + } + + return true; +} + +bool cmBinUtilsWindowsPELinker::ResolveDependency(std::string const& name, + std::string const& origin, + std::string& path, + bool& resolved) +{ + auto dirs = this->Archive->GetSearchDirectories(); + +#ifdef _WIN32 + char buf[MAX_PATH]; + unsigned int len; + if ((len = GetWindowsDirectoryA(buf, MAX_PATH)) > 0) { + dirs.insert(dirs.begin(), std::string(buf, len)); + } + if ((len = GetSystemDirectoryA(buf, MAX_PATH)) > 0) { + dirs.insert(dirs.begin(), std::string(buf, len)); + } +#endif + + dirs.insert(dirs.begin(), origin); + + for (auto const& searchPath : dirs) { + path = searchPath + '/' + name; + if (cmSystemTools::PathExists(path)) { + resolved = true; + return true; + } + } + + resolved = false; + return true; +} diff --git a/Source/cmBinUtilsWindowsPELinker.h b/Source/cmBinUtilsWindowsPELinker.h new file mode 100644 index 0000000..d742195 --- /dev/null +++ b/Source/cmBinUtilsWindowsPELinker.h @@ -0,0 +1,33 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPELinker_h +#define cmBinUtilsWindowsPELinker_h + +#include "cmBinUtilsLinker.h" +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" +#include "cmStateTypes.h" + +#include <memory> // IWYU pragma: keep +#include <string> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPELinker : public cmBinUtilsLinker +{ +public: + cmBinUtilsWindowsPELinker(cmRuntimeDependencyArchive* archive); + + bool Prepare() override; + + bool ScanDependencies(std::string const& file, + cmStateEnums::TargetType type) override; + +private: + std::unique_ptr<cmBinUtilsWindowsPEGetRuntimeDependenciesTool> Tool; + + bool ResolveDependency(std::string const& name, std::string const& origin, + std::string& path, bool& resolved); +}; + +#endif // cmBinUtilsWindowsPELinker_h diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx new file mode 100644 index 0000000..1f27003 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx @@ -0,0 +1,67 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmUVProcessChain.h" + +#include <cmsys/RegularExpression.hxx> + +#include <sstream> + +cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool:: + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive) + : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive) +{ +} + +bool cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::GetFileInfo( + const std::string& file, std::vector<std::string>& needed) +{ + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT); + + std::vector<std::string> command; + if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) { + this->SetError("Could not find objdump"); + return false; + } + command.emplace_back("-p"); + command.push_back(file); + builder.AddCommand(command); + + auto process = builder.Start(); + if (!process.Valid()) { + std::ostringstream e; + e << "Failed to start objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + + std::string line; + static const cmsys::RegularExpression regex( + "^\t*DLL Name: ([^\n]*\\.[Dd][Ll][Ll])\r$"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (regex.find(line.c_str(), match)) { + needed.push_back(match.match(1)); + } + } + + if (!process.Wait()) { + std::ostringstream e; + e << "Failed to wait on objdump process for:\n " << file; + this->SetError(e.str()); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + std::ostringstream e; + e << "Failed to run objdump on:\n " << file; + this->SetError(e.str()); + return false; + } + + return true; +} diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h new file mode 100644 index 0000000..1d1a5b0 --- /dev/null +++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h +#define cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h + +#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool + : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool +{ +public: + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool( + cmRuntimeDependencyArchive* archive); + + bool GetFileInfo(const std::string& file, + std::vector<std::string>& needed) override; +}; + +#endif // cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index 358f095..e8fc350 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx @@ -620,7 +620,7 @@ bool cmCacheManager::CacheIterator::GetValueAsBool() const std::vector<std::string> cmCacheManager::CacheEntry::GetPropertyList() const { - return this->Properties.GetPropertyList(); + return this->Properties.GetKeys(); } const char* cmCacheManager::CacheEntry::GetProperty( diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx index 5fafaf9..894447c 100644 --- a/Source/cmDefinitions.cxx +++ b/Source/cmDefinitions.cxx @@ -57,8 +57,7 @@ bool cmDefinitions::HasKey(const std::string& key, StackIter begin, void cmDefinitions::Set(const std::string& key, const char* value) { - Def def(value); - this->Map[key] = def; + this->Map[key] = Def(value); } std::vector<std::string> cmDefinitions::UnusedKeys() const diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 012355b..3fe84a9 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -45,6 +45,7 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) std::string expectedTargets; std::string sep; std::vector<std::string> targets; + bool generatedInterfaceRequired = false; this->GetTargets(targets); for (std::string const& tei : targets) { cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei); @@ -60,11 +61,13 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) this->LG->GetMakefile()->GetBacktrace()); return false; } - if (this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY) { - this->GenerateRequiredCMakeVersion(os, "3.0.0"); - } + generatedInterfaceRequired |= + this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY; } + if (generatedInterfaceRequired) { + this->GenerateRequiredCMakeVersion(os, "3.0.0"); + } this->GenerateExpectedTargetsCode(os, expectedTargets); } diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index a12e0c4..c366183 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -12,7 +12,6 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" -#include "cmProperty.h" #include "cmPropertyMap.h" #include "cmStateTypes.h" #include "cmSystemTools.h" @@ -1205,12 +1204,9 @@ bool cmExportFileGenerator::PopulateExportProperties( std::string& errorMessage) { auto& targetProperties = gte->Target->GetProperties(); - const auto& exportProperties = targetProperties.find("EXPORT_PROPERTIES"); - if (exportProperties != targetProperties.end()) { - std::vector<std::string> propsToExport; - cmSystemTools::ExpandListArgument(exportProperties->second.GetValue(), - propsToExport); - for (auto& prop : propsToExport) { + if (const char* exportProperties = + targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) { + for (auto& prop : cmSystemTools::ExpandedListArgument(exportProperties)) { /* Black list reserved properties */ if (cmSystemTools::StringStartsWith(prop, "IMPORTED_") || cmSystemTools::StringStartsWith(prop, "INTERFACE_")) { diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 7a3954e..980ad21 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -12,7 +12,9 @@ #include <assert.h> #include <cmath> #include <ctype.h> +#include <map> #include <memory> // IWYU pragma: keep +#include <set> #include <sstream> #include <stdio.h> #include <stdlib.h> @@ -34,6 +36,8 @@ #include "cmMessageType.h" #include "cmPolicies.h" #include "cmRange.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmState.h" #include "cmSystemTools.h" #include "cmTimestamp.h" #include "cm_sys_stat.h" @@ -184,6 +188,9 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "CREATE_LINK") { return this->HandleCreateLinkCommand(args); } + if (subCommand == "GET_RUNTIME_DEPENDENCIES") { + return this->HandleGetRuntimeDependenciesCommand(args); + } std::string e = "does not recognize sub-command " + subCommand; this->SetError(e); @@ -2690,3 +2697,171 @@ bool cmFileCommand::HandleCreateLinkCommand( return true; } + +bool cmFileCommand::HandleGetRuntimeDependenciesCommand( + std::vector<std::string> const& args) +{ + static const std::set<std::string> supportedPlatforms = { "Windows", "Linux", + "Darwin" }; + std::string platform = + this->Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME"); + if (!supportedPlatforms.count(platform)) { + std::ostringstream e; + e << "GET_RUNTIME_DEPENDENCIES is not supported on system \"" << platform + << "\""; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (this->Makefile->GetState()->GetMode() == cmState::Project) { + this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, + "You have used file(GET_RUNTIME_DEPENDENCIES)" + " in project mode. This is probably not what " + "you intended to do. Instead, please consider" + " using it in an install(CODE) or " + "install(SCRIPT) command. For example:" + "\n install(CODE [[" + "\n file(GET_RUNTIME_DEPENDENCIES" + "\n # ..." + "\n )" + "\n ]])"); + } + + struct Arguments + { + std::string ResolvedDependenciesVar; + std::string UnresolvedDependenciesVar; + std::string ConflictingDependenciesPrefix; + std::string BundleExecutable; + std::vector<std::string> Executables; + std::vector<std::string> Libraries; + std::vector<std::string> Directories; + std::vector<std::string> Modules; + std::vector<std::string> PreIncludeRegexes; + std::vector<std::string> PreExcludeRegexes; + std::vector<std::string> PostIncludeRegexes; + std::vector<std::string> PostExcludeRegexes; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("RESOLVED_DEPENDENCIES_VAR"_s, &Arguments::ResolvedDependenciesVar) + .Bind("UNRESOLVED_DEPENDENCIES_VAR"_s, + &Arguments::UnresolvedDependenciesVar) + .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s, + &Arguments::ConflictingDependenciesPrefix) + .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable) + .Bind("EXECUTABLES"_s, &Arguments::Executables) + .Bind("LIBRARIES"_s, &Arguments::Libraries) + .Bind("MODULES"_s, &Arguments::Modules) + .Bind("DIRECTORIES"_s, &Arguments::Directories) + .Bind("PRE_INCLUDE_REGEXES"_s, &Arguments::PreIncludeRegexes) + .Bind("PRE_EXCLUDE_REGEXES"_s, &Arguments::PreExcludeRegexes) + .Bind("POST_INCLUDE_REGEXES"_s, &Arguments::PostIncludeRegexes) + .Bind("POST_EXCLUDE_REGEXES"_s, &Arguments::PostExcludeRegexes); + + std::vector<std::string> unrecognizedArguments; + std::vector<std::string> keywordsMissingValues; + auto parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, + &keywordsMissingValues); + auto argIt = unrecognizedArguments.begin(); + if (argIt != unrecognizedArguments.end()) { + std::ostringstream e; + e << "Unrecognized argument: \"" << *argIt << "\""; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + argIt = keywordsMissingValues.begin(); + if (argIt != keywordsMissingValues.end()) { + std::ostringstream e; + e << "Keyword missing value: " << *argIt; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + cmRuntimeDependencyArchive archive( + this, parsedArgs.Directories, parsedArgs.BundleExecutable, + parsedArgs.PreIncludeRegexes, parsedArgs.PreExcludeRegexes, + parsedArgs.PostIncludeRegexes, parsedArgs.PostExcludeRegexes); + if (!archive.Prepare()) { + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (!archive.GetRuntimeDependencies( + parsedArgs.Executables, parsedArgs.Libraries, parsedArgs.Modules)) { + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + std::vector<std::string> deps, unresolvedDeps, conflictingDeps; + for (auto const& val : archive.GetResolvedPaths()) { + bool unique = true; + auto it = val.second.begin(); + assert(it != val.second.end()); + auto const& firstPath = *it; + while (++it != val.second.end()) { + if (!cmSystemTools::SameFile(firstPath, *it)) { + unique = false; + break; + } + } + + if (unique) { + deps.push_back(firstPath); + } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) { + conflictingDeps.push_back(val.first); + std::vector<std::string> paths; + paths.insert(paths.begin(), val.second.begin(), val.second.end()); + std::string varName = + parsedArgs.ConflictingDependenciesPrefix + "_" + val.first; + std::string pathsStr = cmJoin(paths, ";"); + this->Makefile->AddDefinition(varName, pathsStr.c_str()); + } else { + std::ostringstream e; + e << "Multiple conflicting paths found for " << val.first << ":"; + for (auto const& path : val.second) { + e << "\n " << path; + } + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + if (!archive.GetUnresolvedPaths().empty()) { + if (!parsedArgs.UnresolvedDependenciesVar.empty()) { + unresolvedDeps.insert(unresolvedDeps.begin(), + archive.GetUnresolvedPaths().begin(), + archive.GetUnresolvedPaths().end()); + } else { + auto it = archive.GetUnresolvedPaths().begin(); + assert(it != archive.GetUnresolvedPaths().end()); + std::ostringstream e; + e << "Could not resolve file " << *it; + this->SetError(e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + + if (!parsedArgs.ResolvedDependenciesVar.empty()) { + std::string val = cmJoin(deps, ";"); + this->Makefile->AddDefinition(parsedArgs.ResolvedDependenciesVar, + val.c_str()); + } + if (!parsedArgs.UnresolvedDependenciesVar.empty()) { + std::string val = cmJoin(unresolvedDeps, ";"); + this->Makefile->AddDefinition(parsedArgs.UnresolvedDependenciesVar, + val.c_str()); + } + if (!parsedArgs.ConflictingDependenciesPrefix.empty()) { + std::string val = cmJoin(conflictingDeps, ";"); + this->Makefile->AddDefinition( + parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val.c_str()); + } + return true; +} diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index 12c5115..cfff894 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -62,6 +62,8 @@ protected: bool HandleSizeCommand(std::vector<std::string> const& args); bool HandleReadSymlinkCommand(std::vector<std::string> const& args); bool HandleCreateLinkCommand(std::vector<std::string> const& args); + bool HandleGetRuntimeDependenciesCommand( + std::vector<std::string> const& args); private: void AddEvaluationFile(const std::string& inputName, diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index e590802..be7964a 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -67,6 +67,9 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) } this->AlreadyInCache = false; + // Find what search path locations have been enabled/disable + this->SelectDefaultSearchModes(); + // Find the current root path mode. this->SelectDefaultRootPathMode(); diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index 954558f..c6b9049 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx @@ -3,6 +3,7 @@ #include "cmFindCommon.h" #include <algorithm> +#include <array> #include <string.h> #include <utility> @@ -144,6 +145,26 @@ void cmFindCommon::SelectDefaultMacMode() } } +void cmFindCommon::SelectDefaultSearchModes() +{ + const std::array<std::pair<bool&, std::string>, 5> search_paths = { + { { this->NoPackageRootPath, "CMAKE_FIND_USE_PACAKGE_ROOT_PATH" }, + { this->NoCMakePath, "CMAKE_FIND_USE_CMAKE_PATH" }, + { this->NoCMakeEnvironmentPath, + "CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH" }, + { this->NoSystemEnvironmentPath, + "CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH" }, + { this->NoCMakeSystemPath, "CMAKE_FIND_USE_CMAKE_SYSTEM_PATH" } } + }; + + for (auto& path : search_paths) { + const char* def = this->Makefile->GetDefinition(path.second); + if (def) { + path.first = !cmSystemTools::IsOn(def); + } + } +} + void cmFindCommon::RerootPaths(std::vector<std::string>& paths) { #if 0 diff --git a/Source/cmFindCommon.h b/Source/cmFindCommon.h index 89ff174..d95eeb1 100644 --- a/Source/cmFindCommon.h +++ b/Source/cmFindCommon.h @@ -90,6 +90,9 @@ protected: /** Compute the current default bundle/framework search policy. */ void SelectDefaultMacMode(); + /** Compute the current default search modes based on global variables. */ + void SelectDefaultSearchModes(); + // Path arguments prior to path manipulation routines std::vector<std::string> UserHintsArgs; std::vector<std::string> UserGuessArgs; diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 036a07d..de0f371 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -5032,13 +5032,7 @@ void cmGeneratorTarget::ComputeVersionedName(std::string& vName, std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const { - cmPropertyMap const& propsObject = this->Target->GetProperties(); - std::vector<std::string> props; - props.reserve(propsObject.size()); - for (auto const& it : propsObject) { - props.push_back(it.first); - } - return props; + return this->Target->GetProperties().GetKeys(); } void cmGeneratorTarget::ReportPropertyOrigin( diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 7c5a55b..0774436 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -16,6 +16,8 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmOutputConverter.h" +#include "cmPolicies.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -632,17 +634,34 @@ void cmInstallTargetGenerator::AddRPathCheckRule( return; } - // Get the install RPATH from the link information. - std::string newRpath = cli->GetChrpathString(); - // Write a rule to remove the installed file if its rpath is not the // new rpath. This is needed for existing build/install trees when // the installed rpath changes but the file is not rebuilt. - /* clang-format off */ os << indent << "file(RPATH_CHECK\n" - << indent << " FILE \"" << toDestDirPath << "\"\n" - << indent << " RPATH \"" << newRpath << "\")\n"; - /* clang-format on */ + << indent << " FILE \"" << toDestDirPath << "\"\n"; + + // CMP0095: ``RPATH`` entries are properly escaped in the intermediary + // CMake install script. + switch (this->Target->GetPolicyStatusCMP0095()) { + case cmPolicies::WARN: + // No author warning needed here, we warn later in + // cmInstallTargetGenerator::AddChrpathPatchRule(). + CM_FALLTHROUGH; + case cmPolicies::OLD: { + // Get the install RPATH from the link information. + std::string newRpath = cli->GetChrpathString(); + os << indent << " RPATH \"" << newRpath << "\")\n"; + break; + } + default: { + // Get the install RPATH from the link information and + // escape any CMake syntax in the install RPATH. + std::string escapedNewRpath = + cmOutputConverter::EscapeForCMake(cli->GetChrpathString()); + os << indent << " RPATH " << escapedNewRpath << ")\n"; + break; + } + } } void cmInstallTargetGenerator::AddChrpathPatchRule( @@ -731,11 +750,28 @@ void cmInstallTargetGenerator::AddChrpathPatchRule( return; } + // Escape any CMake syntax in the RPATHs. + std::string escapedOldRpath = cmOutputConverter::EscapeForCMake(oldRpath); + std::string escapedNewRpath = cmOutputConverter::EscapeForCMake(newRpath); + // Write a rule to run chrpath to set the install-tree RPATH os << indent << "file(RPATH_CHANGE\n" << indent << " FILE \"" << toDestDirPath << "\"\n" - << indent << " OLD_RPATH \"" << oldRpath << "\"\n" - << indent << " NEW_RPATH \"" << newRpath << "\")\n"; + << indent << " OLD_RPATH " << escapedOldRpath << "\n"; + + // CMP0095: ``RPATH`` entries are properly escaped in the intermediary + // CMake install script. + switch (this->Target->GetPolicyStatusCMP0095()) { + case cmPolicies::WARN: + this->IssueCMP0095Warning(newRpath); + CM_FALLTHROUGH; + case cmPolicies::OLD: + os << indent << " NEW_RPATH \"" << newRpath << "\")\n"; + break; + default: + os << indent << " NEW_RPATH " << escapedNewRpath << ")\n"; + break; + } } } @@ -838,3 +874,26 @@ void cmInstallTargetGenerator::AddUniversalInstallRule( << "\"" << this->Target->Target->GetName() << "\" " << "\"" << toDestDirPath << "\")\n"; } + +void cmInstallTargetGenerator::IssueCMP0095Warning( + const std::string& unescapedRpath) +{ + // Reduce warning noise to cases where used RPATHs may actually be affected + // by CMP0095. This filter is meant to skip warnings in cases when + // non-curly-braces syntax (e.g. $ORIGIN) or no keyword is used which has + // worked already before CMP0095. We intend to issue a warning in all cases + // with curly-braces syntax, even if the workaround of double-escaping is in + // place, since we deprecate the need for it with CMP0095. + const bool potentially_affected(unescapedRpath.find("${") != + std::string::npos); + + if (potentially_affected) { + std::ostringstream w; + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0095) << "\n"; + w << "RPATH entries for target '" << this->Target->GetName() << "' " + << "will not be escaped in the intermediary " + << "cmake_install.cmake script."; + this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace()); + } +} diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index ed3ab52..9ccad63 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h @@ -104,6 +104,7 @@ protected: const std::string& toDestDirPath); void AddUniversalInstallRule(std::ostream& os, Indent indent, const std::string& toDestDirPath); + void IssueCMP0095Warning(const std::string& unescapedRpath); std::string TargetName; cmGeneratorTarget* Target; diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx index 636a8e1..8d065e1 100644 --- a/Source/cmJsonObjects.cxx +++ b/Source/cmJsonObjects.cxx @@ -14,7 +14,6 @@ #include "cmLinkLineComputer.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmProperty.h" #include "cmPropertyMap.h" #include "cmSourceFile.h" #include "cmState.h" @@ -363,12 +362,12 @@ static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, // Build up the list of properties that may have been specified Json::Value properties = Json::arrayValue; - for (auto& prop : testInfo->GetProperties()) { + for (auto& prop : testInfo->GetProperties().GetList()) { Json::Value entry = Json::objectValue; entry[kKEY_KEY] = prop.first; // Remove config variables from the value too. - auto cge_value = ge.Parse(prop.second.GetValue()); + auto cge_value = ge.Parse(prop.second); const std::string& processed_value = cge_value->Evaluate(lg, config); entry[kVALUE_KEY] = processed_value; properties.append(entry); diff --git a/Source/cmLDConfigLDConfigTool.cxx b/Source/cmLDConfigLDConfigTool.cxx new file mode 100644 index 0000000..586ea96 --- /dev/null +++ b/Source/cmLDConfigLDConfigTool.cxx @@ -0,0 +1,70 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmLDConfigLDConfigTool.h" +#include "cmMakefile.h" +#include "cmRuntimeDependencyArchive.h" +#include "cmSystemTools.h" +#include "cmUVProcessChain.h" + +#include "cmsys/RegularExpression.hxx" + +#include <istream> +#include <string> +#include <vector> + +cmLDConfigLDConfigTool::cmLDConfigLDConfigTool( + cmRuntimeDependencyArchive* archive) + : cmLDConfigTool(archive) +{ +} + +bool cmLDConfigLDConfigTool::GetLDConfigPaths(std::vector<std::string>& paths) +{ + std::string ldConfigPath = + this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_COMMAND"); + if (ldConfigPath.empty()) { + ldConfigPath = cmSystemTools::FindProgram( + "ldconfig", { "/sbin", "/usr/sbin", "/usr/local/sbin" }); + if (ldConfigPath.empty()) { + this->Archive->SetError("Could not find ldconfig"); + return false; + } + } + + std::vector<std::string> ldConfigCommand; + cmSystemTools::ExpandListArgument(ldConfigPath, ldConfigCommand); + ldConfigCommand.emplace_back("-v"); + ldConfigCommand.emplace_back("-N"); // Don't rebuild the cache. + ldConfigCommand.emplace_back("-X"); // Don't update links. + + cmUVProcessChainBuilder builder; + builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .AddCommand(ldConfigCommand); + auto process = builder.Start(); + if (!process.Valid()) { + this->Archive->SetError("Failed to start ldconfig process"); + return false; + } + + std::string line; + static const cmsys::RegularExpression regex("^([^\t:]*):"); + while (std::getline(*process.OutputStream(), line)) { + cmsys::RegularExpressionMatch match; + if (regex.find(line.c_str(), match)) { + paths.push_back(match.match(1)); + } + } + + if (!process.Wait()) { + this->Archive->SetError("Failed to wait on ldconfig process"); + return false; + } + auto status = process.GetStatus(); + if (!status[0] || status[0]->ExitStatus != 0) { + this->Archive->SetError("Failed to run ldconfig"); + return false; + } + + return true; +} diff --git a/Source/cmLDConfigLDConfigTool.h b/Source/cmLDConfigLDConfigTool.h new file mode 100644 index 0000000..d945a9b --- /dev/null +++ b/Source/cmLDConfigLDConfigTool.h @@ -0,0 +1,22 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmLDConfigLDConfigTool_h +#define cmLDConfigLDConfigTool_h + +#include "cmLDConfigTool.h" + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmLDConfigLDConfigTool : public cmLDConfigTool +{ +public: + cmLDConfigLDConfigTool(cmRuntimeDependencyArchive* archive); + + bool GetLDConfigPaths(std::vector<std::string>& paths) override; +}; + +#endif diff --git a/Source/cmLDConfigTool.cxx b/Source/cmLDConfigTool.cxx new file mode 100644 index 0000000..8d5d563 --- /dev/null +++ b/Source/cmLDConfigTool.cxx @@ -0,0 +1,9 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmLDConfigTool.h" + +cmLDConfigTool::cmLDConfigTool(cmRuntimeDependencyArchive* archive) + : Archive(archive) +{ +} diff --git a/Source/cmLDConfigTool.h b/Source/cmLDConfigTool.h new file mode 100644 index 0000000..c816562 --- /dev/null +++ b/Source/cmLDConfigTool.h @@ -0,0 +1,24 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmLDConfigTool_h +#define cmLDConfigTool_h + +#include <string> +#include <vector> + +class cmRuntimeDependencyArchive; + +class cmLDConfigTool +{ +public: + cmLDConfigTool(cmRuntimeDependencyArchive* archive); + virtual ~cmLDConfigTool() = default; + + virtual bool GetLDConfigPaths(std::vector<std::string>& paths) = 0; + +protected: + cmRuntimeDependencyArchive* Archive; +}; + +#endif diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx index 2a8bee6..469faca 100644 --- a/Source/cmLinkLineComputer.cxx +++ b/Source/cmLinkLineComputer.cxx @@ -99,14 +99,34 @@ std::string cmLinkLineComputer::ComputeLinkPath( std::string const& libPathTerminator) { std::string linkPath; - std::vector<std::string> const& libDirs = cli.GetDirectories(); - for (std::string const& libDir : libDirs) { - std::string libpath = this->ConvertToOutputForExisting(libDir); - linkPath += " " + libPathFlag; - linkPath += libpath; - linkPath += libPathTerminator; - linkPath += " "; + + if (cli.GetLinkLanguage() == "Swift") { + for (const cmComputeLinkInformation::Item& item : cli.GetItems()) { + const cmGeneratorTarget* target = item.Target; + if (!target) { + continue; + } + + if (target->GetType() == cmStateEnums::STATIC_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY) { + cmStateEnums::ArtifactType type = cmStateEnums::RuntimeBinaryArtifact; + if (target->GetType() == cmStateEnums::SHARED_LIBRARY && + target->IsDLLPlatform()) { + type = cmStateEnums::ImportLibraryArtifact; + } + + linkPath += " " + libPathFlag + + item.Target->GetDirectory(cli.GetConfig(), type) + + libPathTerminator + " "; + } + } } + + for (std::string const& libDir : cli.GetDirectories()) { + linkPath += " " + libPathFlag + this->ConvertToOutputForExisting(libDir) + + libPathTerminator + " "; + } + return linkPath; } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index fe5c8af..3abf2dd 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1444,10 +1444,23 @@ void cmLocalGenerator::OutputLinkLibraries( std::string linkLanguage = cli.GetLinkLanguage(); - const std::string& libPathFlag = - this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); - const std::string& libPathTerminator = - this->Makefile->GetSafeDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); + std::string libPathFlag; + if (const char* value = this->Makefile->GetDefinition( + "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) { + libPathFlag = value; + } else { + libPathFlag = + this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); + } + + std::string libPathTerminator; + if (const char* value = this->Makefile->GetDefinition( + "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) { + libPathTerminator = value; + } else { + libPathTerminator = + this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); + } // Add standard libraries for this language. std::string standardLibsVar = "CMAKE_"; diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index 7d88b08..d7bcf7e 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx @@ -73,22 +73,10 @@ std::string cmOutputConverter::ConvertDirectorySeparatorsForShell( static bool cmOutputConverterIsShellOperator(const std::string& str) { - static std::set<std::string> shellOperators; - if (shellOperators.empty()) { - shellOperators.insert("<"); - shellOperators.insert(">"); - shellOperators.insert("<<"); - shellOperators.insert(">>"); - shellOperators.insert("|"); - shellOperators.insert("||"); - shellOperators.insert("&&"); - shellOperators.insert("&>"); - shellOperators.insert("1>"); - shellOperators.insert("2>"); - shellOperators.insert("2>&1"); - shellOperators.insert("1>&2"); - } - return shellOperators.count(str) > 0; + static std::set<std::string> const shellOperators{ + "<", ">", "<<", ">>", "|", "||", "&&", "&>", "1>", "2>", "2>&1", "1>&2" + }; + return (shellOperators.count(str) != 0); } std::string cmOutputConverter::EscapeForShell(const std::string& str, diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index b705119..c16a46f 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -279,7 +279,11 @@ class cmMakefile; SELECT(POLICY, CMP0094, \ "FindPython3, FindPython2 and FindPyton use " \ "LOCATION for lookup strategy.", \ - 3, 15, 0, cmPolicies::WARN) + 3, 15, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0095, \ + "RPATH entries are properly escaped in the intermediary CMake " \ + "install script.", \ + 3, 16, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -307,7 +311,8 @@ class cmMakefile; F(CMP0073) \ F(CMP0076) \ F(CMP0081) \ - F(CMP0083) + F(CMP0083) \ + F(CMP0095) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies diff --git a/Source/cmProperty.cxx b/Source/cmProperty.cxx deleted file mode 100644 index 27f0ecd..0000000 --- a/Source/cmProperty.cxx +++ /dev/null @@ -1,26 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmProperty.h" - -void cmProperty::Set(const char* value) -{ - this->Value = value; - this->ValueHasBeenSet = true; -} - -void cmProperty::Append(const char* value, bool asString) -{ - if (!this->Value.empty() && *value && !asString) { - this->Value += ";"; - } - this->Value += value; - this->ValueHasBeenSet = true; -} - -const char* cmProperty::GetValue() const -{ - if (this->ValueHasBeenSet) { - return this->Value.c_str(); - } - return nullptr; -} diff --git a/Source/cmProperty.h b/Source/cmProperty.h index d11c5ef..80f131a 100644 --- a/Source/cmProperty.h +++ b/Source/cmProperty.h @@ -5,8 +5,6 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include <string> - class cmProperty { public: @@ -22,22 +20,6 @@ public: CACHED_VARIABLE, INSTALL }; - - // set this property - void Set(const char* value); - - // append to this property - void Append(const char* value, bool asString = false); - - // get the value - const char* GetValue() const; - - // construct with the value not set - cmProperty() { this->ValueHasBeenSet = false; } - -protected: - std::string Value; - bool ValueHasBeenSet; }; #endif diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx index 3f6d7c8..3ed4c05 100644 --- a/Source/cmPropertyMap.cxx +++ b/Source/cmPropertyMap.cxx @@ -3,40 +3,21 @@ #include "cmPropertyMap.h" #include <algorithm> -#include <assert.h> #include <utility> -cmProperty* cmPropertyMap::GetOrCreateProperty(const std::string& name) +void cmPropertyMap::Clear() { - cmPropertyMap::iterator it = this->find(name); - cmProperty* prop; - if (it == this->end()) { - prop = &(*this)[name]; - } else { - prop = &(it->second); - } - return prop; -} - -std::vector<std::string> cmPropertyMap::GetPropertyList() const -{ - std::vector<std::string> keyList; - for (auto const& i : *this) { - keyList.push_back(i.first); - } - std::sort(keyList.begin(), keyList.end()); - return keyList; + Map_.clear(); } void cmPropertyMap::SetProperty(const std::string& name, const char* value) { if (!value) { - this->erase(name); + Map_.erase(name); return; } - cmProperty* prop = this->GetOrCreateProperty(name); - prop->Set(value); + Map_[name] = value; } void cmPropertyMap::AppendProperty(const std::string& name, const char* value, @@ -47,17 +28,53 @@ void cmPropertyMap::AppendProperty(const std::string& name, const char* value, return; } - cmProperty* prop = this->GetOrCreateProperty(name); - prop->Append(value, asString); + { + std::string& pVal = Map_[name]; + if (!pVal.empty() && !asString) { + pVal += ';'; + } + pVal += value; + } +} + +void cmPropertyMap::RemoveProperty(const std::string& name) +{ + Map_.erase(name); } const char* cmPropertyMap::GetPropertyValue(const std::string& name) const { - assert(!name.empty()); + { + auto it = Map_.find(name); + if (it != Map_.end()) { + return it->second.c_str(); + } + } + return nullptr; +} - cmPropertyMap::const_iterator it = this->find(name); - if (it == this->end()) { - return nullptr; +std::vector<std::string> cmPropertyMap::GetKeys() const +{ + std::vector<std::string> keyList; + keyList.reserve(Map_.size()); + for (auto const& item : Map_) { + keyList.push_back(item.first); + } + std::sort(keyList.begin(), keyList.end()); + return keyList; +} + +std::vector<std::pair<std::string, std::string>> cmPropertyMap::GetList() const +{ + typedef std::pair<std::string, std::string> StringPair; + std::vector<StringPair> kvList; + kvList.reserve(Map_.size()); + for (auto const& item : Map_) { + kvList.emplace_back(item.first, item.second); } - return it->second.GetValue(); + std::sort(kvList.begin(), kvList.end(), + [](StringPair const& a, StringPair const& b) { + return a.first < b.first; + }); + return kvList; } diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h index 5a05150..9aed349 100644 --- a/Source/cmPropertyMap.h +++ b/Source/cmPropertyMap.h @@ -5,25 +5,47 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmProperty.h" - -#include <map> #include <string> +#include <unordered_map> +#include <utility> #include <vector> -class cmPropertyMap : public std::map<std::string, cmProperty> +/** \class cmPropertyMap + * \brief String property map. + */ +class cmPropertyMap { public: - cmProperty* GetOrCreateProperty(const std::string& name); + // -- General + + //! Clear property list + void Clear(); - std::vector<std::string> GetPropertyList() const; + // -- Properties + //! Set the property value void SetProperty(const std::string& name, const char* value); + //! Append to the property value void AppendProperty(const std::string& name, const char* value, bool asString = false); + //! Get the property value const char* GetPropertyValue(const std::string& name) const; + + //! Remove the property @a name from the map + void RemoveProperty(const std::string& name); + + // -- Lists + + //! Get a sorted list of property keys + std::vector<std::string> GetKeys() const; + + //! Get a sorted by key list of property key,value pairs + std::vector<std::pair<std::string, std::string>> GetList() const; + +private: + std::unordered_map<std::string, std::string> Map_; }; #endif diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx new file mode 100644 index 0000000..b4c6c32 --- /dev/null +++ b/Source/cmRuntimeDependencyArchive.cxx @@ -0,0 +1,378 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmRuntimeDependencyArchive.h" + +#include "cmAlgorithms.h" +#include "cmBinUtilsLinuxELFLinker.h" +#include "cmBinUtilsMacOSMachOLinker.h" +#include "cmBinUtilsWindowsPELinker.h" +#include "cmCommand.h" +#include "cmMakefile.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" + +#if defined(_WIN32) +# include "cmGlobalGenerator.h" +# ifdef CMAKE_BUILD_WITH_CMAKE +# include "cmGlobalVisualStudioVersionedGenerator.h" +# endif +# include "cmVSSetupHelper.h" +# include "cmsys/Glob.hxx" +#endif + +#include <algorithm> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#if defined(_WIN32) +static void AddVisualStudioPath(std::vector<std::string>& paths, + const std::string& prefix, + unsigned int version, cmGlobalGenerator* gg) +{ + // If generating for the VS IDE, use the same instance. + std::string vsloc; + bool found = false; +# ifdef CMAKE_BUILD_WITH_CMAKE + if (gg->GetName().find(prefix) == 0) { + cmGlobalVisualStudioVersionedGenerator* vsgen = + static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg); + if (vsgen->GetVSInstance(vsloc)) { + found = true; + } + } +# endif + + // Otherwise, find a VS instance ourselves. + if (!found) { + cmVSSetupAPIHelper vsSetupAPIHelper(version); + if (vsSetupAPIHelper.GetVSInstanceInfo(vsloc)) { + cmSystemTools::ConvertToUnixSlashes(vsloc); + found = true; + } + } + + if (found) { + cmsys::Glob glob; + glob.SetListDirs(true); + glob.FindFiles(vsloc + "/VC/Tools/MSVC/*"); + for (auto const& vcdir : glob.GetFiles()) { + paths.push_back(vcdir + "/bin/Hostx64/x64"); + paths.push_back(vcdir + "/bin/Hostx86/x64"); + paths.push_back(vcdir + "/bin/Hostx64/x86"); + paths.push_back(vcdir + "/bin/Hostx86/x86"); + } + } +} + +static void AddRegistryPath(std::vector<std::string>& paths, + const std::string& path, cmMakefile* mf) +{ + // We should view the registry as the target application would view + // it. + cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32; + cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64; + if (mf->PlatformIs64Bit()) { + view = cmSystemTools::KeyWOW64_64; + other_view = cmSystemTools::KeyWOW64_32; + } + + // Expand using the view of the target application. + std::string expanded = path; + cmSystemTools::ExpandRegistryValues(expanded, view); + cmSystemTools::GlobDirs(expanded, paths); + + // Executables can be either 32-bit or 64-bit, so expand using the + // alternative view. + expanded = path; + cmSystemTools::ExpandRegistryValues(expanded, other_view); + cmSystemTools::GlobDirs(expanded, paths); +} + +static void AddEnvPath(std::vector<std::string>& paths, const std::string& var, + const std::string& suffix) +{ + std::string value; + if (cmSystemTools::GetEnv(var, value)) { + paths.push_back(value + suffix); + } +} +#endif + +static cmsys::RegularExpression TransformCompile(const std::string& str) +{ + return cmsys::RegularExpression(str); +} + +cmRuntimeDependencyArchive::cmRuntimeDependencyArchive( + cmCommand* command, std::vector<std::string> searchDirectories, + std::string bundleExecutable, + const std::vector<std::string>& preIncludeRegexes, + const std::vector<std::string>& preExcludeRegexes, + const std::vector<std::string>& postIncludeRegexes, + const std::vector<std::string>& postExcludeRegexes) + : Command(command) + , SearchDirectories(std::move(searchDirectories)) + , BundleExecutable(std::move(bundleExecutable)) + , PreIncludeRegexes(preIncludeRegexes.size()) + , PreExcludeRegexes(preExcludeRegexes.size()) + , PostIncludeRegexes(postIncludeRegexes.size()) + , PostExcludeRegexes(postExcludeRegexes.size()) +{ + std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(), + this->PreIncludeRegexes.begin(), TransformCompile); + std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(), + this->PreExcludeRegexes.begin(), TransformCompile); + std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(), + this->PostIncludeRegexes.begin(), TransformCompile); + std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(), + this->PostExcludeRegexes.begin(), TransformCompile); +} + +bool cmRuntimeDependencyArchive::Prepare() +{ + std::string platform = this->GetMakefile()->GetSafeDefinition( + "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM"); + if (platform.empty()) { + std::string systemName = + this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME"); + if (systemName == "Windows") { + platform = "windows+pe"; + } else if (systemName == "Darwin") { + platform = "macos+macho"; + } else if (systemName == "Linux") { + platform = "linux+elf"; + } + } + if (platform == "linux+elf") { + this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this); + } else if (platform == "windows+pe") { + this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this); + } else if (platform == "macos+macho") { + this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this); + } else { + std::ostringstream e; + e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: " + << platform; + this->SetError(e.str()); + return false; + } + + return this->Linker->Prepare(); +} + +bool cmRuntimeDependencyArchive::GetRuntimeDependencies( + const std::vector<std::string>& executables, + const std::vector<std::string>& libraries, + const std::vector<std::string>& modules) +{ + for (auto const& exe : executables) { + if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) { + return false; + } + } + for (auto const& lib : libraries) { + if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) { + return false; + } + } + for (auto const& mod : modules) { + if (!this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY)) { + return false; + } + } + + return true; +} + +void cmRuntimeDependencyArchive::SetError(const std::string& e) +{ + this->Command->SetError(e); +} + +std::string cmRuntimeDependencyArchive::GetBundleExecutable() +{ + return this->BundleExecutable; +} + +const std::vector<std::string>& +cmRuntimeDependencyArchive::GetSearchDirectories() +{ + return this->SearchDirectories; +} + +std::string cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool() +{ + return this->GetMakefile()->GetSafeDefinition( + "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL"); +} + +bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand( + const std::string& search, std::vector<std::string>& command) +{ + // First see if it was supplied by the user + std::string toolCommand = this->GetMakefile()->GetSafeDefinition( + "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND"); + if (!toolCommand.empty()) { + cmSystemTools::ExpandListArgument(toolCommand, command); + return true; + } + + // Now go searching for it + std::vector<std::string> paths; +#ifdef _WIN32 + cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator(); + + // Add newer Visual Studio paths + AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg); + AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg); + + // Add older Visual Studio paths + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin"); + paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"); + paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/" + "../../VC/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin"); + paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN"); + paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"); + AddRegistryPath( + paths, + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/" + "../../VC7/bin", + this->GetMakefile()); + AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin"); + paths.push_back( + "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"); + paths.push_back( + "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"); +#endif + + std::string program = cmSystemTools::FindProgram(search, paths); + if (!program.empty()) { + command = { program }; + return true; + } + + // Couldn't find it + return false; +} + +bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) +{ + cmsys::RegularExpressionMatch match; + + for (auto const& regex : this->PreIncludeRegexes) { + if (regex.find(name.c_str(), match)) { + return false; + } + } + + for (auto const& regex : this->PreExcludeRegexes) { + if (regex.find(name.c_str(), match)) { + return true; + } + } + + return false; +} + +bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) +{ + cmsys::RegularExpressionMatch match; + + for (auto const& regex : this->PostIncludeRegexes) { + if (regex.find(name.c_str(), match)) { + return false; + } + } + + for (auto const& regex : this->PostExcludeRegexes) { + if (regex.find(name.c_str(), match)) { + return true; + } + } + + return false; +} + +void cmRuntimeDependencyArchive::AddResolvedPath(const std::string& name, + const std::string& path, + bool& unique) +{ + auto it = + this->ResolvedPaths + .insert(std::pair<std::string, std::set<std::string>>{ name, {} }) + .first; + unique = true; + for (auto const& other : it->second) { + if (cmSystemTools::SameFile(path, other)) { + unique = false; + break; + } + } + it->second.insert(path); +} + +void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name) +{ + this->UnresolvedPaths.insert(name); +} + +cmMakefile* cmRuntimeDependencyArchive::GetMakefile() +{ + return this->Command->GetMakefile(); +} + +const std::map<std::string, std::set<std::string>>& +cmRuntimeDependencyArchive::GetResolvedPaths() +{ + return this->ResolvedPaths; +} + +const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths() +{ + return this->UnresolvedPaths; +} diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h new file mode 100644 index 0000000..ec3ecd4 --- /dev/null +++ b/Source/cmRuntimeDependencyArchive.h @@ -0,0 +1,70 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#ifndef cmRuntimeDependencyArchive_h +#define cmRuntimeDependencyArchive_h + +#include "cmBinUtilsLinker.h" + +#include "cmsys/RegularExpression.hxx" + +#include <map> +#include <memory> // IWYU pragma: keep +#include <set> +#include <string> +#include <vector> + +class cmCommand; +class cmMakefile; + +class cmRuntimeDependencyArchive +{ +public: + explicit cmRuntimeDependencyArchive( + cmCommand* command, std::vector<std::string> searchDirectories, + std::string bundleExecutable, + const std::vector<std::string>& preIncludeRegexes, + const std::vector<std::string>& preExcludeRegexes, + const std::vector<std::string>& postIncludeRegexes, + const std::vector<std::string>& postExcludeRegexes); + bool Prepare(); + bool GetRuntimeDependencies(const std::vector<std::string>& executables, + const std::vector<std::string>& libraries, + const std::vector<std::string>& modules); + + void SetError(const std::string& e); + + std::string GetBundleExecutable(); + const std::vector<std::string>& GetSearchDirectories(); + std::string GetGetRuntimeDependenciesTool(); + bool GetGetRuntimeDependenciesCommand(const std::string& search, + std::vector<std::string>& command); + bool IsPreExcluded(const std::string& name); + bool IsPostExcluded(const std::string& name); + + void AddResolvedPath(const std::string& name, const std::string& path, + bool& unique); + void AddUnresolvedPath(const std::string& name); + + cmMakefile* GetMakefile(); + const std::map<std::string, std::set<std::string>>& GetResolvedPaths(); + const std::set<std::string>& GetUnresolvedPaths(); + +private: + cmCommand* Command; + std::unique_ptr<cmBinUtilsLinker> Linker; + + std::string GetRuntimeDependenciesTool; + std::vector<std::string> GetRuntimeDependenciesCommand; + + std::vector<std::string> SearchDirectories; + std::string BundleExecutable; + std::vector<cmsys::RegularExpression> PreIncludeRegexes; + std::vector<cmsys::RegularExpression> PreExcludeRegexes; + std::vector<cmsys::RegularExpression> PostIncludeRegexes; + std::vector<cmsys::RegularExpression> PostExcludeRegexes; + std::map<std::string, std::set<std::string>> ResolvedPaths; + std::set<std::string> UnresolvedPaths; +}; + +#endif // cmRuntimeDependencyArchive_h diff --git a/Source/cmState.cxx b/Source/cmState.cxx index fa7df0b..091c2e0 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -267,7 +267,7 @@ void cmState::RemoveCacheEntryProperty(std::string const& key, cmStateSnapshot cmState::Reset() { - this->GlobalProperties.clear(); + this->GlobalProperties.Clear(); this->PropertyDefinitions.clear(); this->GlobVerificationManager->Reset(); @@ -289,7 +289,7 @@ cmStateSnapshot cmState::Reset() it->LinkDirectoriesBacktraces.clear(); it->DirectoryEnd = pos; it->NormalTargetNames.clear(); - it->Properties.clear(); + it->Properties.Clear(); it->Children.clear(); } diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index 182d3fe..6ca1c9f 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx @@ -6,7 +6,6 @@ #include <algorithm> #include <assert.h> #include <iterator> -#include <utility> #include "cmAlgorithms.h" #include "cmProperty.h" @@ -667,12 +666,7 @@ bool cmStateDirectory::GetPropertyAsBool(const std::string& prop) const std::vector<std::string> cmStateDirectory::GetPropertyKeys() const { - std::vector<std::string> keys; - keys.reserve(this->DirectoryState->Properties.size()); - for (auto const& it : this->DirectoryState->Properties) { - keys.push_back(it.first); - } - return keys; + return this->DirectoryState->Properties.GetKeys(); } void cmStateDirectory::AddNormalTargetName(std::string const& name) diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index cd67586..b10b30f 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -231,14 +231,21 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, impl->IsAndroid = (impl->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android"); - std::string gKey; - gKey.reserve(128); - gKey += "CMAKE_"; - auto InitProperty = [this, mf, &gKey](const std::string& property, - const char* default_value) { + std::string defKey; + defKey.reserve(128); + defKey += "CMAKE_"; + auto initProp = [this, mf, &defKey](const std::string& property) { // Replace everything after "CMAKE_" - gKey.replace(gKey.begin() + 6, gKey.end(), property); - if (const char* value = mf->GetDefinition(gKey)) { + defKey.replace(defKey.begin() + 6, defKey.end(), property); + if (const char* value = mf->GetDefinition(defKey)) { + this->SetProperty(property, value); + } + }; + auto initPropValue = [this, mf, &defKey](const std::string& property, + const char* default_value) { + // Replace everything after "CMAKE_" + defKey.replace(defKey.begin() + 6, defKey.end(), property); + if (const char* value = mf->GetDefinition(defKey)) { this->SetProperty(property, value); } else if (default_value) { this->SetProperty(property, default_value); @@ -248,107 +255,107 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, // Setup default property values. if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("ANDROID_API", nullptr); - InitProperty("ANDROID_API_MIN", nullptr); - InitProperty("ANDROID_ARCH", nullptr); - InitProperty("ANDROID_STL_TYPE", nullptr); - InitProperty("ANDROID_SKIP_ANT_STEP", nullptr); - InitProperty("ANDROID_PROCESS_MAX", nullptr); - InitProperty("ANDROID_PROGUARD", nullptr); - InitProperty("ANDROID_PROGUARD_CONFIG_PATH", nullptr); - InitProperty("ANDROID_SECURE_PROPS_PATH", nullptr); - InitProperty("ANDROID_NATIVE_LIB_DIRECTORIES", nullptr); - InitProperty("ANDROID_NATIVE_LIB_DEPENDENCIES", nullptr); - InitProperty("ANDROID_JAVA_SOURCE_DIR", nullptr); - InitProperty("ANDROID_JAR_DIRECTORIES", nullptr); - InitProperty("ANDROID_JAR_DEPENDENCIES", nullptr); - InitProperty("ANDROID_ASSETS_DIRECTORIES", nullptr); - InitProperty("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr); - InitProperty("BUILD_RPATH", nullptr); - InitProperty("BUILD_RPATH_USE_ORIGIN", nullptr); - InitProperty("INSTALL_NAME_DIR", nullptr); - InitProperty("INSTALL_RPATH", ""); - InitProperty("INSTALL_RPATH_USE_LINK_PATH", "OFF"); - InitProperty("INTERPROCEDURAL_OPTIMIZATION", nullptr); - InitProperty("SKIP_BUILD_RPATH", "OFF"); - InitProperty("BUILD_WITH_INSTALL_RPATH", "OFF"); - InitProperty("ARCHIVE_OUTPUT_DIRECTORY", nullptr); - InitProperty("LIBRARY_OUTPUT_DIRECTORY", nullptr); - InitProperty("RUNTIME_OUTPUT_DIRECTORY", nullptr); - InitProperty("PDB_OUTPUT_DIRECTORY", nullptr); - InitProperty("COMPILE_PDB_OUTPUT_DIRECTORY", nullptr); - InitProperty("FRAMEWORK", nullptr); - InitProperty("Fortran_FORMAT", nullptr); - InitProperty("Fortran_MODULE_DIRECTORY", nullptr); - InitProperty("Fortran_COMPILER_LAUNCHER", nullptr); - InitProperty("GNUtoMS", nullptr); - InitProperty("OSX_ARCHITECTURES", nullptr); - InitProperty("IOS_INSTALL_COMBINED", nullptr); - InitProperty("AUTOMOC", nullptr); - InitProperty("AUTOUIC", nullptr); - InitProperty("AUTORCC", nullptr); - InitProperty("AUTOGEN_ORIGIN_DEPENDS", nullptr); - InitProperty("AUTOGEN_PARALLEL", nullptr); - InitProperty("AUTOMOC_COMPILER_PREDEFINES", nullptr); - InitProperty("AUTOMOC_DEPEND_FILTERS", nullptr); - InitProperty("AUTOMOC_MACRO_NAMES", nullptr); - InitProperty("AUTOMOC_MOC_OPTIONS", nullptr); - InitProperty("AUTOUIC_OPTIONS", nullptr); - InitProperty("AUTOUIC_SEARCH_PATHS", nullptr); - InitProperty("AUTORCC_OPTIONS", nullptr); - InitProperty("LINK_DEPENDS_NO_SHARED", nullptr); - InitProperty("LINK_INTERFACE_LIBRARIES", nullptr); - InitProperty("MSVC_RUNTIME_LIBRARY", nullptr); - InitProperty("WIN32_EXECUTABLE", nullptr); - InitProperty("MACOSX_BUNDLE", nullptr); - InitProperty("MACOSX_RPATH", nullptr); - InitProperty("NO_SYSTEM_FROM_IMPORTED", nullptr); - InitProperty("BUILD_WITH_INSTALL_NAME_DIR", nullptr); - InitProperty("C_CLANG_TIDY", nullptr); - InitProperty("C_COMPILER_LAUNCHER", nullptr); - InitProperty("C_CPPLINT", nullptr); - InitProperty("C_CPPCHECK", nullptr); - InitProperty("C_INCLUDE_WHAT_YOU_USE", nullptr); - InitProperty("LINK_WHAT_YOU_USE", nullptr); - InitProperty("C_STANDARD", nullptr); - InitProperty("C_STANDARD_REQUIRED", nullptr); - InitProperty("C_EXTENSIONS", nullptr); - InitProperty("CXX_CLANG_TIDY", nullptr); - InitProperty("CXX_COMPILER_LAUNCHER", nullptr); - InitProperty("CXX_CPPLINT", nullptr); - InitProperty("CXX_CPPCHECK", nullptr); - InitProperty("CXX_INCLUDE_WHAT_YOU_USE", nullptr); - InitProperty("CXX_STANDARD", nullptr); - InitProperty("CXX_STANDARD_REQUIRED", nullptr); - InitProperty("CXX_EXTENSIONS", nullptr); - InitProperty("CUDA_STANDARD", nullptr); - InitProperty("CUDA_STANDARD_REQUIRED", nullptr); - InitProperty("CUDA_EXTENSIONS", nullptr); - InitProperty("CUDA_COMPILER_LAUNCHER", nullptr); - InitProperty("CUDA_SEPARABLE_COMPILATION", nullptr); - InitProperty("LINK_SEARCH_START_STATIC", nullptr); - InitProperty("LINK_SEARCH_END_STATIC", nullptr); - InitProperty("FOLDER", nullptr); - InitProperty("Swift_MODULE_DIRECTORY", nullptr); - InitProperty("VS_JUST_MY_CODE_DEBUGGING", nullptr); + initProp("ANDROID_API"); + initProp("ANDROID_API_MIN"); + initProp("ANDROID_ARCH"); + initProp("ANDROID_STL_TYPE"); + initProp("ANDROID_SKIP_ANT_STEP"); + initProp("ANDROID_PROCESS_MAX"); + initProp("ANDROID_PROGUARD"); + initProp("ANDROID_PROGUARD_CONFIG_PATH"); + initProp("ANDROID_SECURE_PROPS_PATH"); + initProp("ANDROID_NATIVE_LIB_DIRECTORIES"); + initProp("ANDROID_NATIVE_LIB_DEPENDENCIES"); + initProp("ANDROID_JAVA_SOURCE_DIR"); + initProp("ANDROID_JAR_DIRECTORIES"); + initProp("ANDROID_JAR_DEPENDENCIES"); + initProp("ANDROID_ASSETS_DIRECTORIES"); + initProp("ANDROID_ANT_ADDITIONAL_OPTIONS"); + initProp("BUILD_RPATH"); + initProp("BUILD_RPATH_USE_ORIGIN"); + initProp("INSTALL_NAME_DIR"); + initPropValue("INSTALL_RPATH", ""); + initPropValue("INSTALL_RPATH_USE_LINK_PATH", "OFF"); + initProp("INTERPROCEDURAL_OPTIMIZATION"); + initPropValue("SKIP_BUILD_RPATH", "OFF"); + initPropValue("BUILD_WITH_INSTALL_RPATH", "OFF"); + initProp("ARCHIVE_OUTPUT_DIRECTORY"); + initProp("LIBRARY_OUTPUT_DIRECTORY"); + initProp("RUNTIME_OUTPUT_DIRECTORY"); + initProp("PDB_OUTPUT_DIRECTORY"); + initProp("COMPILE_PDB_OUTPUT_DIRECTORY"); + initProp("FRAMEWORK"); + initProp("Fortran_FORMAT"); + initProp("Fortran_MODULE_DIRECTORY"); + initProp("Fortran_COMPILER_LAUNCHER"); + initProp("GNUtoMS"); + initProp("OSX_ARCHITECTURES"); + initProp("IOS_INSTALL_COMBINED"); + initProp("AUTOMOC"); + initProp("AUTOUIC"); + initProp("AUTORCC"); + initProp("AUTOGEN_ORIGIN_DEPENDS"); + initProp("AUTOGEN_PARALLEL"); + initProp("AUTOMOC_COMPILER_PREDEFINES"); + initProp("AUTOMOC_DEPEND_FILTERS"); + initProp("AUTOMOC_MACRO_NAMES"); + initProp("AUTOMOC_MOC_OPTIONS"); + initProp("AUTOUIC_OPTIONS"); + initProp("AUTOUIC_SEARCH_PATHS"); + initProp("AUTORCC_OPTIONS"); + initProp("LINK_DEPENDS_NO_SHARED"); + initProp("LINK_INTERFACE_LIBRARIES"); + initProp("MSVC_RUNTIME_LIBRARY"); + initProp("WIN32_EXECUTABLE"); + initProp("MACOSX_BUNDLE"); + initProp("MACOSX_RPATH"); + initProp("NO_SYSTEM_FROM_IMPORTED"); + initProp("BUILD_WITH_INSTALL_NAME_DIR"); + initProp("C_CLANG_TIDY"); + initProp("C_COMPILER_LAUNCHER"); + initProp("C_CPPLINT"); + initProp("C_CPPCHECK"); + initProp("C_INCLUDE_WHAT_YOU_USE"); + initProp("LINK_WHAT_YOU_USE"); + initProp("C_STANDARD"); + initProp("C_STANDARD_REQUIRED"); + initProp("C_EXTENSIONS"); + initProp("CXX_CLANG_TIDY"); + initProp("CXX_COMPILER_LAUNCHER"); + initProp("CXX_CPPLINT"); + initProp("CXX_CPPCHECK"); + initProp("CXX_INCLUDE_WHAT_YOU_USE"); + initProp("CXX_STANDARD"); + initProp("CXX_STANDARD_REQUIRED"); + initProp("CXX_EXTENSIONS"); + initProp("CUDA_STANDARD"); + initProp("CUDA_STANDARD_REQUIRED"); + initProp("CUDA_EXTENSIONS"); + initProp("CUDA_COMPILER_LAUNCHER"); + initProp("CUDA_SEPARABLE_COMPILATION"); + initProp("LINK_SEARCH_START_STATIC"); + initProp("LINK_SEARCH_END_STATIC"); + initProp("FOLDER"); + initProp("Swift_MODULE_DIRECTORY"); + initProp("VS_JUST_MY_CODE_DEBUGGING"); #ifdef __APPLE__ if (this->GetGlobalGenerator()->IsXcode()) { - InitProperty("XCODE_GENERATE_SCHEME", nullptr); - InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER", nullptr); - InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", nullptr); - InitProperty("XCODE_SCHEME_THREAD_SANITIZER", nullptr); - InitProperty("XCODE_SCHEME_THREAD_SANITIZER_STOP", nullptr); - InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", nullptr); - InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", nullptr); - InitProperty("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", nullptr); - InitProperty("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", nullptr); - InitProperty("XCODE_SCHEME_MALLOC_SCRIBBLE", nullptr); - InitProperty("XCODE_SCHEME_MALLOC_GUARD_EDGES", nullptr); - InitProperty("XCODE_SCHEME_GUARD_MALLOC", nullptr); - InitProperty("XCODE_SCHEME_ZOMBIE_OBJECTS", nullptr); - InitProperty("XCODE_SCHEME_MALLOC_STACK", nullptr); - InitProperty("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", nullptr); - InitProperty("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", nullptr); + initProp("XCODE_GENERATE_SCHEME"); + initProp("XCODE_SCHEME_ADDRESS_SANITIZER"); + initProp("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN"); + initProp("XCODE_SCHEME_THREAD_SANITIZER"); + initProp("XCODE_SCHEME_THREAD_SANITIZER_STOP"); + initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"); + initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"); + initProp("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); + initProp("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"); + initProp("XCODE_SCHEME_MALLOC_SCRIBBLE"); + initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES"); + initProp("XCODE_SCHEME_GUARD_MALLOC"); + initProp("XCODE_SCHEME_ZOMBIE_OBJECTS"); + initProp("XCODE_SCHEME_MALLOC_STACK"); + initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"); + initProp("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"); } #endif } @@ -376,7 +383,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } std::string property = prop; property += configUpper; - InitProperty(property, nullptr); + initProp(property); } // Initialize per-configuration name postfix property from the @@ -388,7 +395,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) { std::string property = cmSystemTools::UpperCase(configName); property += "_POSTFIX"; - InitProperty(property, nullptr); + initProp(property); } } } @@ -427,16 +434,16 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("C_VISIBILITY_PRESET", nullptr); - InitProperty("CXX_VISIBILITY_PRESET", nullptr); - InitProperty("CUDA_VISIBILITY_PRESET", nullptr); - InitProperty("VISIBILITY_INLINES_HIDDEN", nullptr); + initProp("C_VISIBILITY_PRESET"); + initProp("CXX_VISIBILITY_PRESET"); + initProp("CUDA_VISIBILITY_PRESET"); + initProp("VISIBILITY_INLINES_HIDDEN"); } if (impl->TargetType == cmStateEnums::EXECUTABLE) { - InitProperty("ANDROID_GUI", nullptr); - InitProperty("CROSSCOMPILING_EMULATOR", nullptr); - InitProperty("ENABLE_EXPORTS", nullptr); + initProp("ANDROID_GUI"); + initProp("CROSSCOMPILING_EMULATOR"); + initProp("ENABLE_EXPORTS"); } if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || impl->TargetType == cmStateEnums::MODULE_LIBRARY) { @@ -444,12 +451,12 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || impl->TargetType == cmStateEnums::EXECUTABLE) { - InitProperty("WINDOWS_EXPORT_ALL_SYMBOLS", nullptr); + initProp("WINDOWS_EXPORT_ALL_SYMBOLS"); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("POSITION_INDEPENDENT_CODE", nullptr); + initProp("POSITION_INDEPENDENT_CODE"); } // Record current policies for later use. @@ -465,12 +472,12 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - InitProperty("JOB_POOL_COMPILE", nullptr); - InitProperty("JOB_POOL_LINK", nullptr); + initProp("JOB_POOL_COMPILE"); + initProp("JOB_POOL_LINK"); } if (impl->TargetType <= cmStateEnums::UTILITY) { - InitProperty("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr); + initProp("DOTNET_TARGET_FRAMEWORK_VERSION"); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && @@ -491,7 +498,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (assignment != std::string::npos) { const std::string propName = vsGlobal + i.substr(0, assignment); const std::string propValue = i.substr(assignment + 1); - InitProperty(propName, propValue.c_str()); + initPropValue(propName, propValue.c_str()); } } } @@ -1484,7 +1491,6 @@ const char* cmTarget::GetComputedProperty( const char* cmTarget::GetProperty(const std::string& prop) const { - static std::unordered_set<std::string> specialProps; #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP MAKE_STATIC_PROP(LINK_LIBRARIES); MAKE_STATIC_PROP(TYPE); @@ -1502,23 +1508,23 @@ const char* cmTarget::GetProperty(const std::string& prop) const MAKE_STATIC_PROP(SOURCE_DIR); MAKE_STATIC_PROP(SOURCES); #undef MAKE_STATIC_PROP - if (specialProps.empty()) { - specialProps.insert(propLINK_LIBRARIES); - specialProps.insert(propTYPE); - specialProps.insert(propINCLUDE_DIRECTORIES); - specialProps.insert(propCOMPILE_FEATURES); - specialProps.insert(propCOMPILE_OPTIONS); - specialProps.insert(propCOMPILE_DEFINITIONS); - specialProps.insert(propLINK_OPTIONS); - specialProps.insert(propLINK_DIRECTORIES); - specialProps.insert(propIMPORTED); - specialProps.insert(propIMPORTED_GLOBAL); - specialProps.insert(propMANUALLY_ADDED_DEPENDENCIES); - specialProps.insert(propNAME); - specialProps.insert(propBINARY_DIR); - specialProps.insert(propSOURCE_DIR); - specialProps.insert(propSOURCES); - } + static std::unordered_set<std::string> const specialProps{ + propLINK_LIBRARIES, + propTYPE, + propINCLUDE_DIRECTORIES, + propCOMPILE_FEATURES, + propCOMPILE_OPTIONS, + propCOMPILE_DEFINITIONS, + propLINK_OPTIONS, + propLINK_DIRECTORIES, + propIMPORTED, + propIMPORTED_GLOBAL, + propMANUALLY_ADDED_DEPENDENCIES, + propNAME, + propBINARY_DIR, + propSOURCE_DIR, + propSOURCES + }; if (specialProps.count(prop)) { if (prop == propLINK_LIBRARIES) { if (impl->LinkImplementationPropertyEntries.empty()) { diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx index 3f763af..eac300f 100644 --- a/Source/cmTargetPropertyComputer.cxx +++ b/Source/cmTargetPropertyComputer.cxx @@ -56,22 +56,21 @@ bool cmTargetPropertyComputer::WhiteListedInterfaceProperty( if (std::islower(prop[0])) { return true; } - static std::unordered_set<std::string> builtIns; - if (builtIns.empty()) { - builtIns.insert("COMPATIBLE_INTERFACE_BOOL"); - builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MAX"); - builtIns.insert("COMPATIBLE_INTERFACE_NUMBER_MIN"); - builtIns.insert("COMPATIBLE_INTERFACE_STRING"); - builtIns.insert("EXPORT_NAME"); - builtIns.insert("EXPORT_PROPERTIES"); - builtIns.insert("IMPORTED"); - builtIns.insert("IMPORTED_GLOBAL"); - builtIns.insert("MANUALLY_ADDED_DEPENDENCIES"); - builtIns.insert("NAME"); - builtIns.insert("PRIVATE_HEADER"); - builtIns.insert("PUBLIC_HEADER"); - builtIns.insert("TYPE"); - } + static std::unordered_set<std::string> const builtIns{ + "COMPATIBLE_INTERFACE_BOOL", + "COMPATIBLE_INTERFACE_NUMBER_MAX", + "COMPATIBLE_INTERFACE_NUMBER_MIN", + "COMPATIBLE_INTERFACE_STRING", + "EXPORT_NAME", + "EXPORT_PROPERTIES", + "IMPORTED", + "IMPORTED_GLOBAL", + "MANUALLY_ADDED_DEPENDENCIES", + "NAME", + "PRIVATE_HEADER", + "PUBLIC_HEADER", + "TYPE" + }; if (builtIns.count(prop)) { return true; diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx index 7d45cf5..01f2b96 100644 --- a/Source/cmTest.cxx +++ b/Source/cmTest.cxx @@ -8,7 +8,8 @@ #include "cmSystemTools.h" cmTest::cmTest(cmMakefile* mf) - : Backtrace(mf->GetBacktrace()) + : CommandExpandLists(false) + , Backtrace(mf->GetBacktrace()) { this->Makefile = mf; this->OldStyle = true; @@ -59,3 +60,13 @@ void cmTest::AppendProperty(const std::string& prop, const char* value, { this->Properties.AppendProperty(prop, value, asString); } + +bool cmTest::GetCommandExpandLists() const +{ + return this->CommandExpandLists; +} + +void cmTest::SetCommandExpandLists(bool b) +{ + this->CommandExpandLists = b; +} diff --git a/Source/cmTest.h b/Source/cmTest.h index 88dc730..02d8f46 100644 --- a/Source/cmTest.h +++ b/Source/cmTest.h @@ -51,10 +51,15 @@ public: bool GetOldStyle() const { return this->OldStyle; } void SetOldStyle(bool b) { this->OldStyle = b; } + /** Set/Get whether lists in command lines should be expanded. */ + bool GetCommandExpandLists() const; + void SetCommandExpandLists(bool b); + private: cmPropertyMap Properties; std::string Name; std::vector<std::string> Command; + bool CommandExpandLists; bool OldStyle; diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index 571cd09..498953e 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -10,7 +10,6 @@ #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmOutputConverter.h" -#include "cmProperty.h" #include "cmPropertyMap.h" #include "cmRange.h" #include "cmStateTypes.h" @@ -76,12 +75,22 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, // Start the test command. os << indent << "add_test(" << this->Test->GetName() << " "; - // Get the test command line to be executed. - std::vector<std::string> const& command = this->Test->GetCommand(); + // Evaluate command line arguments + std::vector<std::string> argv = + EvaluateCommandLineArguments(this->Test->GetCommand(), ge, config); + + // Expand arguments if COMMAND_EXPAND_LISTS is set + if (this->Test->GetCommandExpandLists()) { + argv = cmSystemTools::ExpandedLists(argv.begin(), argv.end()); + // Expanding lists on an empty command may have left it empty + if (argv.empty()) { + argv.emplace_back(); + } + } // Check whether the command executable is a target whose name is to // be translated. - std::string exe = command[0]; + std::string exe = argv[0]; cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(exe); if (target && target->GetType() == cmStateEnums::EXECUTABLE) { // Use the target file on disk. @@ -101,29 +110,26 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, } } else { // Use the command name given. - exe = ge.Parse(exe)->Evaluate(this->LG, config); cmSystemTools::ConvertToUnixSlashes(exe); } // Generate the command line with full escapes. os << cmOutputConverter::EscapeForCMake(exe); - for (std::string const& arg : cmMakeRange(command).advance(1)) { - os << " " - << cmOutputConverter::EscapeForCMake( - ge.Parse(arg)->Evaluate(this->LG, config)); + + for (auto const& arg : cmMakeRange(argv).advance(1)) { + os << " " << cmOutputConverter::EscapeForCMake(arg); } // Finish the test command. os << ")\n"; // Output properties for the test. - cmPropertyMap& pm = this->Test->GetProperties(); os << indent << "set_tests_properties(" << this->Test->GetName() << " PROPERTIES "; - for (auto const& i : pm) { + for (auto const& i : this->Test->GetProperties().GetList()) { os << " " << i.first << " " << cmOutputConverter::EscapeForCMake( - ge.Parse(i.second.GetValue())->Evaluate(this->LG, config)); + ge.Parse(i.second)->Evaluate(this->LG, config)); } this->GenerateInternalProperties(os); os << ")" << std::endl; @@ -173,12 +179,11 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) fout << ")" << std::endl; // Output properties for the test. - cmPropertyMap& pm = this->Test->GetProperties(); fout << indent << "set_tests_properties(" << this->Test->GetName() << " PROPERTIES "; - for (auto const& i : pm) { + for (auto const& i : this->Test->GetProperties().GetList()) { fout << " " << i.first << " " - << cmOutputConverter::EscapeForCMake(i.second.GetValue()); + << cmOutputConverter::EscapeForCMake(i.second); } this->GenerateInternalProperties(fout); fout << ")" << std::endl; @@ -208,3 +213,16 @@ void cmTestGenerator::GenerateInternalProperties(std::ostream& os) os << "\""; } + +std::vector<std::string> cmTestGenerator::EvaluateCommandLineArguments( + const std::vector<std::string>& argv, cmGeneratorExpression& ge, + const std::string& config) const +{ + // Evaluate executable name and arguments + auto evaluatedRange = + cmMakeRange(argv).transform([&](const std::string& arg) { + return ge.Parse(arg)->Evaluate(this->LG, config); + }); + + return { evaluatedRange.begin(), evaluatedRange.end() }; +} diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h index 8b9cf78..7ac68eb 100644 --- a/Source/cmTestGenerator.h +++ b/Source/cmTestGenerator.h @@ -11,6 +11,7 @@ #include <string> #include <vector> +class cmGeneratorExpression; class cmLocalGenerator; class cmTest; @@ -38,6 +39,9 @@ public: private: void GenerateInternalProperties(std::ostream& os); + std::vector<std::string> EvaluateCommandLineArguments( + const std::vector<std::string>& argv, cmGeneratorExpression& ge, + const std::string& config) const; protected: void GenerateScriptConfigs(std::ostream& os, Indent indent) override; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9368414..8b9a41f 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -505,6 +505,11 @@ void cmVisualStudio10TargetGenerator::Generate() if (targetFrameworkVersion) { e1.Element("TargetFrameworkVersion", targetFrameworkVersion); } + if (this->ProjectType == vcxproj && + this->GlobalGenerator->TargetsWindowsCE()) { + e1.Element("EnableRedirectPlatform", "true"); + e1.Element("RedirectPlatformValue", this->Platform); + } if (this->ProjectType == csproj && this->GlobalGenerator->TargetsWindowsCE()) { const char* targetFrameworkId = this->GeneratorTarget->GetProperty( @@ -774,11 +779,11 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferences(Elem& e0) cmSystemTools::ExpandListArgument(vsDotNetReferences, references); } cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties(); - for (auto const& i : props) { + for (auto const& i : props.GetList()) { if (i.first.find("VS_DOTNET_REFERENCE_") == 0) { std::string name = i.first.substr(20); if (!name.empty()) { - std::string path = i.second.GetValue(); + std::string path = i.second; if (!cmsys::SystemTools::FileIsFullPath(path)) { path = this->Makefile->GetCurrentSourceDirectory() + "/" + path; } @@ -870,10 +875,10 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags( typedef std::map<std::string, std::string> CustomTags; CustomTags tags; cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties(); - for (const auto& i : props) { + for (const auto& i : props.GetList()) { if (i.first.find(refPropFullPrefix) == 0) { std::string refTag = i.first.substr(refPropFullPrefix.length()); - std::string refVal = i.second.GetValue(); + std::string refVal = i.second; if (!refTag.empty() && !refVal.empty()) { tags[refTag] = refVal; } @@ -967,12 +972,12 @@ void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup(Elem& e0) } } const cmPropertyMap& props = oi->GetProperties(); - for (const auto& p : props) { + for (const std::string& p : props.GetKeys()) { static const std::string propNamePrefix = "VS_CSHARP_"; - if (p.first.find(propNamePrefix) == 0) { - std::string tagName = p.first.substr(propNamePrefix.length()); + if (p.find(propNamePrefix) == 0) { + std::string tagName = p.substr(propNamePrefix.length()); if (!tagName.empty()) { - std::string value = props.GetPropertyValue(p.first); + std::string value = props.GetPropertyValue(p); if (!value.empty()) { e2.Element(tagName.c_str(), value); } @@ -4681,12 +4686,12 @@ void cmVisualStudio10TargetGenerator::GetCSharpSourceProperties( { if (this->ProjectType == csproj) { const cmPropertyMap& props = sf->GetProperties(); - for (auto const& p : props) { + for (const std::string& p : props.GetKeys()) { static const std::string propNamePrefix = "VS_CSHARP_"; - if (p.first.find(propNamePrefix) == 0) { - std::string tagName = p.first.substr(propNamePrefix.length()); + if (p.find(propNamePrefix) == 0) { + std::string tagName = p.substr(propNamePrefix.length()); if (!tagName.empty()) { - const std::string val = props.GetPropertyValue(p.first); + const std::string val = props.GetPropertyValue(p); if (!val.empty()) { tags[tagName] = val; } else { diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 69f8162..735ad5f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -408,7 +408,12 @@ else() set(NO_NAMELINK 0) endif() -add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN}) +add_RunCMake_test(install -DNO_NAMELINK=${NO_NAMELINK} -DCYGWIN=${CYGWIN} -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN=${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN} + -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DCMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG=${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG} + -DCMAKE_EXECUTABLE_FORMAT=${CMAKE_EXECUTABLE_FORMAT}) + add_RunCMake_test(CPackCommandLine) add_RunCMake_test(CPackConfig) add_RunCMake_test(CPackInstallProperties) @@ -565,3 +570,5 @@ if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])") add_RunCMake_test(CSharpCustomCommand) add_RunCMake_test(CSharpReferenceImport) endif() + +add_RunCMake_test("CTestCommandExpandLists") diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt new file mode 100644 index 0000000..3e470a2 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.14) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in new file mode 100644 index 0000000..7d56c90 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/CMakeLists.txt.in @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.14) +project(@CASE_NAME@ NONE) +include("@RunCMake_SOURCE_DIR@/@CASE_NAME@.cmake") diff --git a/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake new file mode 100644 index 0000000..7c3779e --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/RunCMakeTest.cmake @@ -0,0 +1,5 @@ +include(RunCTest) + +run_ctest(expandGeneratorExpressionResult) +run_ctest(expandEmptyCommand) +run_cmake(multipleExpandOptions) diff --git a/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake new file mode 100644 index 0000000..a32e579 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/compare_options.cmake @@ -0,0 +1,14 @@ +set(range 1 2 3 4 5 6 7 8 9 10) +set(aargs "") +set(bargs "") +foreach(n IN LISTS range) + set(aval "${A${n}ARG}") + set(bval "${B${n}ARG}") + if(aval OR bval) + list(APPEND aargs "\"${aval}\"") + list(APPEND bargs "\"${bval}\"") + endif() +endforeach() +if(NOT "${aargs}" STREQUAL "${bargs}") + message(FATAL_ERROR "COMPARE_OPTIONS: \n\t${aargs} != \n\t${bargs}") +endif() diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt new file mode 100644 index 0000000..b57e2de --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-result.txt @@ -0,0 +1 @@ +(-1|255) diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt new file mode 100644 index 0000000..c656b4c --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stderr.txt @@ -0,0 +1 @@ +Unable to find executable: diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt new file mode 100644 index 0000000..0752580 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-stdout.txt @@ -0,0 +1,13 @@ +Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand-build +.* +Start 1: CommandExpandEmptyList +Could not find executable + +Looked in the following places: +.* +1/1 Test #1: CommandExpandEmptyList +\.+\*\*\*Not Run +[0-9.]+ sec ++ +0% tests passed, 1 tests failed out of 1 ++ +Total Test time \(real\) = +[0-9.]+ sec ++ +The following tests FAILED: +.* +1 - CommandExpandEmptyList \(Not Run\)$ diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake new file mode 100644 index 0000000..b75828e --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandEmptyCommand.cmake @@ -0,0 +1,10 @@ +include(CTest) + +set(argv /bin/true) +list(POP_BACK argv) + +add_test( + NAME CommandExpandEmptyList + COMMAND "$<JOIN:${argv},;>" + COMMAND_EXPAND_LISTS +) diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt new file mode 100644 index 0000000..2f21592 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-stdout.txt @@ -0,0 +1,7 @@ +Test project .*/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult-build +.* +Start 1: CommandExpandList +1/1 Test #1: CommandExpandList +\.+ +Passed +[0-9.]+ sec ++ +100% tests passed, 0 tests failed out of 1 ++ +Total Test time \(real\) = +[0-9.]+ sec diff --git a/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake new file mode 100644 index 0000000..20608ae --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/expandGeneratorExpressionResult.cmake @@ -0,0 +1,19 @@ +include(CTest) + + +set(cmp_args "1ARG=COMMAND_EXPAND_LISTS" "2ARG=test" "3ARG=outfile" + "4ARG=content") +set(AARGS "") +foreach(arg IN LISTS cmp_args) + list(APPEND AARGS "-DA${arg}") +endforeach() + + + +add_test( + NAME CommandExpandList + COMMAND ${CMAKE_COMMAND} ${AARGS} -V + "-DB$<JOIN:${cmp_args},;-DB>" + "-P" "${CMAKE_CURRENT_LIST_DIR}/compare_options.cmake" + COMMAND_EXPAND_LISTS +) diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt new file mode 100644 index 0000000..e48513f --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at multipleExpandOptions\.cmake:3 \(add_test\): + +add_test may be given at most one COMMAND_EXPAND_LISTS\. diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt new file mode 100644 index 0000000..55bb894 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-stdout.txt @@ -0,0 +1,2 @@ +-- Configuring incomplete, errors occurred! +See also ".*/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions-build/CMakeFiles/CMakeOutput\.log". diff --git a/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake new file mode 100644 index 0000000..dcf2dc4 --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/multipleExpandOptions.cmake @@ -0,0 +1,8 @@ +include(CTest) + +add_test( + NAME MultipleExpandOptions + COMMAND /bin/true + COMMAND_EXPAND_LISTS + COMMAND_EXPAND_LISTS +) diff --git a/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in new file mode 100644 index 0000000..d9a8ccb --- /dev/null +++ b/Tests/RunCMake/CTestCommandExpandLists/test.cmake.in @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.14) + +set(CTEST_SITE "test-site") +set(CTEST_BUILD_NAME "test-build-name") +set(CTEST_SOURCE_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@") +set(CTEST_BINARY_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@-build") +set(CTEST_CMAKE_GENERATOR "@RunCMake_GENERATOR@") +set(CTEST_CMAKE_GENERATOR_PLATFORM "@RunCMake_GENERATOR_PLATFORM@") +set(CTEST_CMAKE_GENERATOR_TOOLSET "@RunCMake_GENERATOR_TOOLSET@") +set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}") + +ctest_start(Experimental) +ctest_configure() +ctest_build() +ctest_test() diff --git a/Tests/RunCMake/README.rst b/Tests/RunCMake/README.rst index d8b43fe..ebe40cf 100644 --- a/Tests/RunCMake/README.rst +++ b/Tests/RunCMake/README.rst @@ -55,6 +55,12 @@ but do not actually build anything. To add a test: ``<SubTest>-check.cmake`` Custom result check. + Note that when a specific platform expects differing stdout or stderr that + can be done by adding a platform specific output file. These follow the + naming convention of: + ``<SubTest>-stdout-<platform_lower_case>.txt`` + ``<SubTest>-stderr-<platform_lower_case>.txt`` + Note that trailing newlines will be stripped from actual and expected test output before matching against the stdout and stderr expressions. The code in ``<SubTest>-check.cmake`` may use variables diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index ad3f8f6..e730d6e 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -21,10 +21,20 @@ function(run_cmake test) else() set(expect_result 0) endif() + + string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} platform_name) + if(platform_name MATCHES cygwin) + #remove all additional bits from cygwin name + set(platform_name cygwin) + endif() + foreach(o out err) if(RunCMake-std${o}-file AND EXISTS ${top_src}/${RunCMake-std${o}-file}) file(READ ${top_src}/${RunCMake-std${o}-file} expect_std${o}) string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}") + elseif(EXISTS ${top_src}/${test}-std${o}-${platform_name}.txt) + file(READ ${top_src}/${test}-std${o}-${platform_name}.txt expect_std${o}) + string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}") elseif(EXISTS ${top_src}/${test}-std${o}.txt) file(READ ${top_src}/${test}-std${o}.txt expect_std${o}) string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}") diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 0bcf886..6d72fac 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt @@ -27,6 +27,7 @@ \* CMP0076 \* CMP0081 \* CMP0083 + \* CMP0095 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake b/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake index dab1c33..b1deb99 100644 --- a/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake +++ b/Tests/RunCMake/VS10ProjectWinCE/VsCEDebuggerDeploy-check.cmake @@ -14,6 +14,9 @@ endif() set(FoundCEAdditionalFiles FALSE) set(FoundRemoteDirectory FALSE) set(FoundToolsVersion4 FALSE) +set(FoundEnableRedirectPlatform FALSE) +set(FoundRedirectPlatformValue FALSE) + file(STRINGS "${vcProjectFile}" lines) foreach(line IN LISTS lines) @@ -23,6 +26,10 @@ foreach(line IN LISTS lines) set(FoundRemoteDirectory TRUE) elseif(line MATCHES " *<Project +.*ToolsVersion=\"4.0\".*> *$") set(FoundToolsVersion4 TRUE) + elseif(line MATCHES "^ *<EnableRedirectPlatform>true</EnableRedirectPlatform> *$") + set(FoundEnableRedirectPlatform TRUE) + elseif(line MATCHES "^ *<RedirectPlatformValue>.+</RedirectPlatformValue> *$") + set(FoundRedirectPlatformValue TRUE) endif() endforeach() @@ -41,6 +48,16 @@ if(NOT FoundToolsVersion4) return() endif() +if(NOT FoundEnableRedirectPlatform) + set(RunCMake_TEST_FAILED "Failed to find EnableRedirectPlatform true property.") + return() +endif() + +if(NOT FoundRedirectPlatformValue) + set(RunCMake_TEST_FAILED "Failed to find RedirectPlatformValue property.") + return() +endif() + # # Test solution file deployment items. # diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt new file mode 100644 index 0000000..6912bdf --- /dev/null +++ b/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt @@ -0,0 +1,9 @@ +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt new file mode 100644 index 0000000..6912bdf --- /dev/null +++ b/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt @@ -0,0 +1,9 @@ +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout.txt new file mode 100644 index 0000000..27a83ad --- /dev/null +++ b/Tests/RunCMake/find_file/FromPATHEnv-stdout.txt @@ -0,0 +1,9 @@ +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPATHEnv.cmake b/Tests/RunCMake/find_file/FromPATHEnv.cmake new file mode 100644 index 0000000..9f058dd --- /dev/null +++ b/Tests/RunCMake/find_file/FromPATHEnv.cmake @@ -0,0 +1,24 @@ +set(ENV_PATH "$ENV{PATH}") +foreach(path "/does_not_exist" "/include" "") + unset(PrefixInPATH_File CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_file(PrefixInPATH_File NAMES PrefixInPATH.h) + message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'") +endforeach() + +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF) +foreach(path "/does_not_exist" "/include" "") + unset(PrefixInPATH_File CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_file(PrefixInPATH_File NAMES PrefixInPATH.h) + message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'") +endforeach() + +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON) +foreach(path "/does_not_exist" "/include" "") + unset(PrefixInPATH_File CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_file(PrefixInPATH_File NAMES PrefixInPATH.h NO_SYSTEM_ENVIRONMENT_PATH) + message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'") +endforeach() +set(ENV{PATH} "${ENV_PATH}") diff --git a/Tests/RunCMake/find_file/FromPrefixPath-stdout.txt b/Tests/RunCMake/find_file/FromPrefixPath-stdout.txt new file mode 100644 index 0000000..4bd24aa --- /dev/null +++ b/Tests/RunCMake/find_file/FromPrefixPath-stdout.txt @@ -0,0 +1,6 @@ +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' +-- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND' diff --git a/Tests/RunCMake/find_file/FromPrefixPath.cmake b/Tests/RunCMake/find_file/FromPrefixPath.cmake new file mode 100644 index 0000000..1bf8409 --- /dev/null +++ b/Tests/RunCMake/find_file/FromPrefixPath.cmake @@ -0,0 +1,17 @@ +set(ENV{PATH} "") +foreach(path "/does_not_exist" "/include" "") + unset(PrefixInPATH_File CACHE) + set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_file(PrefixInPATH_File NAMES PrefixInPATH.h) + message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'") +endforeach() + +set(CMAKE_FIND_USE_CMAKE_PATH OFF) +set(CMAKE_PREFIX_PATH ) +foreach(path "/does_not_exist" "/include" "") + unset(PrefixInPATH_File CACHE) + set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_file(PrefixInPATH_File NAMES PrefixInPATH.h) + message(STATUS "PrefixInPATH_File='${PrefixInPATH_File}'") +endforeach() +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON) diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt new file mode 100644 index 0000000..d73bc1d --- /dev/null +++ b/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt @@ -0,0 +1,4 @@ +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt new file mode 100644 index 0000000..d73bc1d --- /dev/null +++ b/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt @@ -0,0 +1,4 @@ +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt index d73bc1d..947a900 100644 --- a/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt +++ b/Tests/RunCMake/find_file/PrefixInPATH-stdout.txt @@ -1,4 +1,4 @@ -- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h' +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' diff --git a/Tests/RunCMake/find_file/RunCMakeTest.cmake b/Tests/RunCMake/find_file/RunCMakeTest.cmake index 5ce96e0..9f56a57 100644 --- a/Tests/RunCMake/find_file/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_file/RunCMakeTest.cmake @@ -1,5 +1,5 @@ include(RunCMake) -if(WIN32 OR CYGWIN) - run_cmake(PrefixInPATH) -endif() +run_cmake(FromPATHEnv) +run_cmake(FromPrefixPath) +run_cmake(PrefixInPATH) diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt new file mode 100644 index 0000000..01e2720 --- /dev/null +++ b/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt @@ -0,0 +1,6 @@ +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' +-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt new file mode 100644 index 0000000..01e2720 --- /dev/null +++ b/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt @@ -0,0 +1,6 @@ +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' +-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout.txt new file mode 100644 index 0000000..4e570a6 --- /dev/null +++ b/Tests/RunCMake/find_library/FromPATHEnv-stdout.txt @@ -0,0 +1,6 @@ +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPATHEnv.cmake b/Tests/RunCMake/find_library/FromPATHEnv.cmake new file mode 100644 index 0000000..fec041d --- /dev/null +++ b/Tests/RunCMake/find_library/FromPATHEnv.cmake @@ -0,0 +1,22 @@ +list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib) +list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .a) +set(ENV_PATH "$ENV{PATH}") +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib/libcreated.a" "created") + +foreach(path "/does_not_exist" "/lib" "") + unset(CREATED_LIBRARY CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_BINARY_DIR}${path}") + find_library(CREATED_LIBRARY NAMES created) + message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'") +endforeach() + +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF) +foreach(path "/does_not_exist" "/lib" "") + unset(CREATED_LIBRARY CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_BINARY_DIR}${path}") + find_library(CREATED_LIBRARY NAMES created) + message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'") +endforeach() +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON) +set(ENV{PATH} "${ENV_PATH}") diff --git a/Tests/RunCMake/find_library/FromPrefixPath-stdout.txt b/Tests/RunCMake/find_library/FromPrefixPath-stdout.txt new file mode 100644 index 0000000..de3df1a --- /dev/null +++ b/Tests/RunCMake/find_library/FromPrefixPath-stdout.txt @@ -0,0 +1,6 @@ +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPrefixPath-build/lib/libcreated.a' +-- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPrefixPath-build/lib/libcreated.a' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' +-- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/FromPrefixPath.cmake b/Tests/RunCMake/find_library/FromPrefixPath.cmake new file mode 100644 index 0000000..04763a9 --- /dev/null +++ b/Tests/RunCMake/find_library/FromPrefixPath.cmake @@ -0,0 +1,24 @@ +list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib) +list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .a) +set(ENV_PATH "$ENV{PATH}") +set(ENV{PATH} "") +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/lib/libcreated.a" "created") + +foreach(path "/does_not_exist" "/lib" "") + unset(CREATED_LIBRARY CACHE) + set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}${path}") + find_library(CREATED_LIBRARY NAMES created) + message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'") +endforeach() + +set(CMAKE_FIND_USE_CMAKE_PATH OFF) +set(CMAKE_PREFIX_PATH ) +foreach(path "/does_not_exist" "/lib" "") + unset(CREATED_LIBRARY CACHE) + set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}${path}") + find_library(CREATED_LIBRARY NAMES created) + message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'") +endforeach() +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON) +set(ENV{PATH} "${ENV_PATH}") diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt new file mode 100644 index 0000000..1ab884c --- /dev/null +++ b/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt @@ -0,0 +1,4 @@ +-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' +-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt new file mode 100644 index 0000000..1ab884c --- /dev/null +++ b/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt @@ -0,0 +1,4 @@ +-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' +-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +-- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt index 1ab884c..c6ff513 100644 --- a/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt +++ b/Tests/RunCMake/find_library/PrefixInPATH-stdout.txt @@ -1,4 +1,4 @@ -- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' --- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a' +-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' +-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' +-- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND' diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake index e7e8db3..643a5b9 100644 --- a/Tests/RunCMake/find_library/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake @@ -1,9 +1,9 @@ include(RunCMake) run_cmake(Created) +run_cmake(FromPrefixPath) +run_cmake(FromPATHEnv) if(CMAKE_HOST_UNIX) run_cmake(LibArchLink) endif() -if(WIN32 OR CYGWIN) - run_cmake(PrefixInPATH) -endif() +run_cmake(PrefixInPATH) diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt new file mode 100644 index 0000000..8f3e7ca --- /dev/null +++ b/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt @@ -0,0 +1,9 @@ +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' +-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt new file mode 100644 index 0000000..8f3e7ca --- /dev/null +++ b/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt @@ -0,0 +1,9 @@ +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' +-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout.txt new file mode 100644 index 0000000..fd41bf4 --- /dev/null +++ b/Tests/RunCMake/find_path/FromPATHEnv-stdout.txt @@ -0,0 +1,9 @@ +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' +-- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND' diff --git a/Tests/RunCMake/find_path/FromPATHEnv.cmake b/Tests/RunCMake/find_path/FromPATHEnv.cmake new file mode 100644 index 0000000..af13d09 --- /dev/null +++ b/Tests/RunCMake/find_path/FromPATHEnv.cmake @@ -0,0 +1,25 @@ +set(ENV_PATH "$ENV{PATH}") +foreach(path "/does_not_exist" "/include" "") + unset(PATH_IN_ENV_PATH CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_path(PATH_IN_ENV_PATH NAMES PrefixInPATH.h) + message(STATUS "PATH_IN_ENV_PATH='${PATH_IN_ENV_PATH}'") +endforeach() + +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF) +foreach(path "/does_not_exist" "/include" "") + unset(PATH_IN_ENV_PATH CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_path(PATH_IN_ENV_PATH NAMES PrefixInPATH.h) + message(STATUS "PATH_IN_ENV_PATH='${PATH_IN_ENV_PATH}'") +endforeach() + +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH ON) +foreach(path "/does_not_exist" "/include" "") + unset(PATH_IN_ENV_PATH CACHE) + set(ENV{PATH} "${CMAKE_CURRENT_SOURCE_DIR}${path}") + find_path(PATH_IN_ENV_PATH NAMES PrefixInPATH.h NO_SYSTEM_ENVIRONMENT_PATH) + message(STATUS "PATH_IN_ENV_PATH='${PATH_IN_ENV_PATH}'") +endforeach() + +set(ENV{PATH} "${ENV_PATH}") diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt new file mode 100644 index 0000000..bb2ceb7 --- /dev/null +++ b/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt @@ -0,0 +1,4 @@ +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt new file mode 100644 index 0000000..bb2ceb7 --- /dev/null +++ b/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt @@ -0,0 +1,4 @@ +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' +-- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt index bb2ceb7..947a900 100644 --- a/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt +++ b/Tests/RunCMake/find_path/PrefixInPATH-stdout.txt @@ -1,4 +1,4 @@ -- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' --- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include' +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' +-- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND' diff --git a/Tests/RunCMake/find_path/RunCMakeTest.cmake b/Tests/RunCMake/find_path/RunCMakeTest.cmake index bf0fa89..8b5b5b7 100644 --- a/Tests/RunCMake/find_path/RunCMakeTest.cmake +++ b/Tests/RunCMake/find_path/RunCMakeTest.cmake @@ -1,8 +1,7 @@ include(RunCMake) -if(WIN32 OR CYGWIN) - run_cmake(PrefixInPATH) -endif() +run_cmake(FromPATHEnv) +run_cmake(PrefixInPATH) if(APPLE) run_cmake(FrameworksWithSubdirs) diff --git a/Tests/RunCMake/find_program/EnvAndHints-stdout.txt b/Tests/RunCMake/find_program/EnvAndHints-stdout.txt index 39329b2..0051636 100644 --- a/Tests/RunCMake/find_program/EnvAndHints-stdout.txt +++ b/Tests/RunCMake/find_program/EnvAndHints-stdout.txt @@ -1 +1,4 @@ -- PROG='[^']*/Tests/RunCMake/find_program/A/testAandB' +-- PROG='PROG-NOTFOUND' +-- PROG='[^']*/Tests/RunCMake/find_program/B/testAandB' +-- PROG='[^']*/Tests/RunCMake/find_program/A/testAandB' diff --git a/Tests/RunCMake/find_program/EnvAndHints.cmake b/Tests/RunCMake/find_program/EnvAndHints.cmake index 14ebd6e..0f12eff 100644 --- a/Tests/RunCMake/find_program/EnvAndHints.cmake +++ b/Tests/RunCMake/find_program/EnvAndHints.cmake @@ -1,8 +1,31 @@ + set(ENV_PATH "$ENV{PATH}") set(ENV{PATH} ${CMAKE_CURRENT_SOURCE_DIR}/A) find_program(PROG NAMES testAandB + ) +message(STATUS "PROG='${PROG}'") +unset(PROG CACHE) + +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF) +find_program(PROG + NAMES testAandB + ) +message(STATUS "PROG='${PROG}'") +unset(PROG CACHE) + +find_program(PROG + NAMES testAandB + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/B ${CMAKE_CURRENT_SOURCE_DIR}/A + ) +message(STATUS "PROG='${PROG}'") +unset(PROG CACHE) +set(ENV{PATH} "${ENV_PATH}") + +find_program(PROG + NAMES testAandB HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A ${CMAKE_CURRENT_SOURCE_DIR}/B ) message(STATUS "PROG='${PROG}'") +unset(PROG CACHE) set(ENV{PATH} "${ENV_PATH}") diff --git a/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt b/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt index cb3c99f..d2312e7 100644 --- a/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt +++ b/Tests/RunCMake/find_program/RelAndAbsPath-stdout.txt @@ -1,6 +1,8 @@ -- PROG_ABS='PROG_ABS-NOTFOUND' -- PROG_ABS_NPD='PROG_ABS_NPD-NOTFOUND' -- PROG_CWD='PROG_CWD-NOTFOUND' +-- PROG_CWD='PROG_CWD-NOTFOUND' +-- PROG_CWD='[^']*/Tests/RunCMake/find_program/testCWD' -- PROG_CWD_NPD='PROG_CWD_NPD-NOTFOUND' -- PROG_CWD_DOT='[^']*/Tests/RunCMake/find_program/testCWD' -- PROG_CWD_DOT_NPD='[^']*/Tests/RunCMake/find_program/testCWD' diff --git a/Tests/RunCMake/find_program/RelAndAbsPath.cmake b/Tests/RunCMake/find_program/RelAndAbsPath.cmake index 9a42c5e..e6bdef7 100644 --- a/Tests/RunCMake/find_program/RelAndAbsPath.cmake +++ b/Tests/RunCMake/find_program/RelAndAbsPath.cmake @@ -38,6 +38,28 @@ find_program(PROG_CWD ) message(STATUS "PROG_CWD='${PROG_CWD}'") + +set(CMAKE_PREFIX_PATH ".") +# On some platforms / dashboards the current working +# directory can be in PATH or other search locations +# so disable all searching to make sure this fails +set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF) +set(CMAKE_FIND_USE_CMAKE_PATH OFF) +set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF) +set(CMAKE_FIND_USE_PACAKGE_ROOT_PATH OFF) +set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF) +find_program(PROG_CWD + NAMES testCWD + ) +message(STATUS "PROG_CWD='${PROG_CWD}'") + +set(CMAKE_PREFIX_PATH ".") +set(CMAKE_FIND_USE_CMAKE_PATH ON) +find_program(PROG_CWD + NAMES testCWD + ) +message(STATUS "PROG_CWD='${PROG_CWD}'") + find_program(PROG_CWD_NPD NAMES testCWD NAMES_PER_DIR diff --git a/Tests/RunCMake/install/CMakeLists.txt b/Tests/RunCMake/install/CMakeLists.txt index 6dd8cdf..c7e99ad 100644 --- a/Tests/RunCMake/install/CMakeLists.txt +++ b/Tests/RunCMake/install/CMakeLists.txt @@ -1,3 +1,6 @@ cmake_minimum_required(VERSION 3.4) +if(RunCMake_TEST MATCHES "^file-GET_RUNTIME_DEPENDENCIES") + cmake_policy(SET CMP0087 NEW) +endif() project(${RunCMake_TEST} NONE) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index c637db1..21c320b 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -48,6 +48,22 @@ in directory: endif() endfunction() +# Wrapper for run_cmake() that skips platforms that are non-ELF or have no RPATH support +function(run_cmake_ELFRPATH_only case) + if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF") + run_cmake(${case}) + else() + # Sanity check against a platform known to be ELF-based + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Expected platform Linux to advertize itself as ELF-based, but it did not.") + else() + message(STATUS "${case} - SKIPPED (No ELF-based platform found)") + endif() + endif() +endfunction() + +run_cmake(TARGETS-FILE_RPATH_CHANGE-old_rpath) +run_cmake_ELFRPATH_only(TARGETS-FILE_RPATH_CHANGE-new_rpath) run_cmake(DIRECTORY-MESSAGE_NEVER) run_cmake(DIRECTORY-PATTERN-MESSAGE_NEVER) run_cmake(DIRECTORY-message) @@ -139,6 +155,36 @@ run_install_test(FILES-PERMISSIONS) run_install_test(TARGETS-RPATH) run_install_test(InstallRequiredSystemLibraries) +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-unresolved) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-conflict) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-macos-notfile) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-project) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-unresolved) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-conflict) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-windows-notfile) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-project) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + if(NOT CMAKE_C_COMPILER_ID MATCHES "^XL") + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux) + endif() + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-unresolved) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-conflict) + run_install_test(file-GET_RUNTIME_DEPENDENCIES-linux-notfile) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-project) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs1) + run_cmake(file-GET_RUNTIME_DEPENDENCIES-badargs2) +else() + run_cmake(file-GET_RUNTIME_DEPENDENCIES-unsupported) +endif() + set(run_install_test_components 1) run_install_test(FILES-EXCLUDE_FROM_ALL) run_install_test(TARGETS-EXCLUDE_FROM_ALL) diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake new file mode 100644 index 0000000..673fdde --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-check-common.cmake @@ -0,0 +1,30 @@ +file(READ ${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake install_script) +#message(STATUS ${install_script}) + +set(wsnl " *[\n\r]+ *") # whitespace + single newline + whitespace +set(wssl " *[\n\r]+[^\n\r]*[\n\r]+ *") # ws nl skipline nl ws +string(CONCAT prefix [[file\(RPATH_CHANGE]]) +set(_msg "cmake_install.cmake does not match ") + +macro(check) + if(NOT install_script MATCHES "${regex}") + message(STATUS "${test} - check \"${target}\" - FAILED:") + string(CONCAT RunCMake_TEST_FAILED "${_msg}" ">>>${regex}<<<") + return() + else() + message(STATUS "${test} - check \"${target}\" - PASSED") + endif() +endmacro() + +macro(skip_without_rpath_change_rule) +# Not all platforms generate a file(RPATH_CHANGE) rule + if(NOT install_script MATCHES [[file\(RPATH_CHANGE]]) + # Sanity check against a platform known to generate a file(RPATH_CHANGE) rule + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Expected generated file(RPATH_CHANGE) rule on platform Linux.") + else() + message(STATUS "${test} - All checks skipped. No file(RPATH_CHANGE) rule found on this platform.") + return() + endif() + endif() +endmacro() diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake new file mode 100644 index 0000000..930ef70 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-check.cmake @@ -0,0 +1,63 @@ +include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake) +skip_without_rpath_change_rule() +string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]]) + +set(target "exe1_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar]]) +check() + +set(target "exe1_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar]]) +check() + +set(target "exe1_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar]]) +check() + +set(target "exe2_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\$ORIGIN/../lib]]) +check() + +set(target "exe2_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\$ORIGIN/../lib]]) +check() + +set(target "exe2_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\\\$ORIGIN/../lib]]) +check() + +set(target "exe3_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\${ORIGIN}/../lib]]) +check() + +set(target "exe3_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\${ORIGIN}/../lib]]) +check() + +set(target "exe3_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "\\\${ORIGIN}/../lib]]) +check() + +set(target "exe4_cmp0095_old") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar/\${PLATFORM}]]) +check() + +set(target "exe4_cmp0095_warn") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar/\${PLATFORM}]]) +check() + +set(target "exe4_cmp0095_new") +string(CONCAT regex "${prefix}${target}\"${wssl}" + [[NEW_RPATH "/foo/bar/\\\${PLATFORM}]]) +check() diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt new file mode 100644 index 0000000..1e123f6 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath-stderr.txt @@ -0,0 +1,23 @@ +^CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): + Policy CMP0095 is not set: RPATH entries are properly escaped in the + intermediary CMake install script\. Run "cmake --help-policy CMP0095" for + policy details\. Use the cmake_policy command to set the policy and + suppress this warning\. + + RPATH entries for target 'exe3_cmp0095_warn' will not be escaped in the + intermediary cmake_install\.cmake script\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Warning \(dev\) at TARGETS-FILE_RPATH_CHANGE-new_rpath\.cmake:[0-9]+ \(install\): + Policy CMP0095 is not set: RPATH entries are properly escaped in the + intermediary CMake install script\. Run "cmake --help-policy CMP0095" for + policy details\. Use the cmake_policy command to set the policy and + suppress this warning\. + + RPATH entries for target 'exe4_cmp0095_warn' will not be escaped in the + intermediary cmake_install\.cmake script\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake new file mode 100644 index 0000000..cba04b2 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.14) +enable_language(C) + +# test matrix +# +# A := +# | no cmake syntax | cmake syntax | +# -----------------------+-----------------+--------------+ +# absolute install RPATH | exe1 | exe4 | +# relative install RPATH | exe2 | exe3 | +# +# all := A * CMP005_OLD + A * CMP0095_WARN + A * CMP0095_NEW + +add_library(utils SHARED obj1.c) +set(targets utils) + +set(exe1_install_rpath "/foo/bar") +set(exe2_install_rpath "\$ORIGIN/../lib") +set(exe3_install_rpath "\${ORIGIN}/../lib") +set(exe4_install_rpath "/foo/bar/\${PLATFORM}") + +macro(A_CMP0095 policy_value) + cmake_policy(PUSH) + if(NOT "x${policy_value}x" STREQUAL "xWARNx") + cmake_policy(SET CMP0095 ${policy_value}) + endif() + string(TOLOWER "${policy_value}" p) + + # exe1: absolute install RPATH, no cmake syntax + set(case "exe1") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + # exe2: relative install RPATH, no cmake syntax + set(case "exe2") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + # exe3: relative install RPATH, cmake syntax + set(case "exe3") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + # exe4: absolute install RPATH, cmake syntax + set(case "exe4") + set(target "${case}_cmp0095_${p}") + list(APPEND targets ${target}) + add_executable(${target} main.c) + target_link_libraries(${target} PRIVATE utils) + set_target_properties(${target} PROPERTIES + INSTALL_RPATH "${${case}_install_rpath}") + + cmake_policy(POP) +endmacro() + +A_CMP0095("OLD") +A_CMP0095("WARN") # exe3 and exe4 are expected to issue an author warning +A_CMP0095("NEW") + +install(TARGETS ${targets}) diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake new file mode 100644 index 0000000..814f405 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath-check.cmake @@ -0,0 +1,15 @@ +include(${RunCMake_SOURCE_DIR}/TARGETS-FILE_RPATH_CHANGE-check-common.cmake) +skip_without_rpath_change_rule() +string(APPEND prefix "${wsnl}" [[FILE "[^"]*/]]) + +set(target "exe1") +string(CONCAT regex "${prefix}${target}\"${wsnl}" + [[OLD_RPATH "]] "${RunCMake_BINARY_DIR}") +check() + +if("x${CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN}" STREQUAL "x\$ORIGIN") + set(target "exe2") + string(CONCAT regex "${prefix}${target}\"${wsnl}" + [[OLD_RPATH "\\\$ORIGIN]]) + check() +endif() diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake new file mode 100644 index 0000000..43ae787 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.14) +enable_language(C) + +add_library(utils SHARED obj1.c) + +# exe1: absolute build RPATH, no cmake syntax +set(CMAKE_BUILD_RPATH_USE_ORIGIN OFF) +set(CMAKE_INSTALL_RPATH "/foo/bar") +add_executable(exe1 main.c) +target_link_libraries(exe1 PRIVATE utils) + +# exe2: relative build RPATH, no cmake syntax +set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) +set(CMAKE_INSTALL_RPATH "/foo/bar") +add_executable(exe2 main.c) +target_link_libraries(exe2 PRIVATE utils) + +install(TARGETS utils exe1 exe2) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt new file mode 100644 index 0000000..b66d1fe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1-stderr.txt @@ -0,0 +1,18 @@ +^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\): + You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is + probably not what you intended to do\. Instead, please consider using it in + an install\(CODE\) or install\(SCRIPT\) command\. For example: + + install\(CODE \[\[ + file\(GET_RUNTIME_DEPENDENCIES + # \.\.\. + \) + ]]\) +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs1\.cmake:[0-9]+ \(file\): + file Unrecognized argument: "invalid" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake new file mode 100644 index 0000000..f3b8ce4 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs1.cmake @@ -0,0 +1,2 @@ +file(GET_RUNTIME_DEPENDENCIES invalid) +message(FATAL_ERROR "This message should not be displayed") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt new file mode 100644 index 0000000..94f0f46 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2-stderr.txt @@ -0,0 +1,18 @@ +^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\): + You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is + probably not what you intended to do\. Instead, please consider using it in + an install\(CODE\) or install\(SCRIPT\) command\. For example: + + install\(CODE \[\[ + file\(GET_RUNTIME_DEPENDENCIES + # \.\.\. + \) + ]]\) +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\. + +CMake Error at file-GET_RUNTIME_DEPENDENCIES-badargs2\.cmake:[0-9]+ \(file\): + file Keyword missing value: BUNDLE_EXECUTABLE +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake new file mode 100644 index 0000000..138ab95 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-badargs2.cmake @@ -0,0 +1,2 @@ +file(GET_RUNTIME_DEPENDENCIES BUNDLE_EXECUTABLE) +message(FATAL_ERROR "This message should not be displayed") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake new file mode 100644 index 0000000..ab630f0 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-check.cmake @@ -0,0 +1,44 @@ +function(check_contents filename contents_regex) + if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}") + file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents) + if(NOT contents MATCHES "${contents_regex}") + string(APPEND RunCMake_TEST_FAILED "File contents: + ${contents} +do not match what we expected: + ${contents_regex} +in file: + ${CMAKE_INSTALL_PREFIX}/${filename}\n") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() + else() + string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() +endfunction() + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_rpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/libtest_runpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath/librpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_parent/librpath_parent\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search/librpath_search\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath/librunpath\.so]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search/librunpath_search\.so]] + ) +check_contents(deps/deps1.txt "^${_check}$") +check_contents(deps/deps2.txt "^${_check}$") +check_contents(deps/deps3.txt "^${_check}$") +set(_check + [[librpath_unresolved\.so]] + [[librunpath_parent_unresolved\.so]] + [[librunpath_unresolved\.so]] + ) +check_contents(deps/udeps1.txt "^${_check}$") +check_contents(deps/udeps2.txt "^${_check}$") +check_contents(deps/udeps3.txt "^${_check}$") +set(_check + "^libconflict\\.so:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict/libconflict\\.so;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/conflict2/libconflict\\.so\n$" + ) +check_contents(deps/cdeps1.txt "${_check}") +check_contents(deps/cdeps2.txt "${_check}") +check_contents(deps/cdeps3.txt "${_check}") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt new file mode 100644 index 0000000..123ae48 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-all-stderr.txt @@ -0,0 +1,119 @@ +^CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/rpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search_postexcluded\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search_postexcluded + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\) + +*CMake Warning at cmake_install\.cmake:[0-9]+ \(file\): + Dependency librunpath_search\.so found in search directory: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-build/root-all/lib/runpath_search + + See file\(GET_RUNTIME_DEPENDENCIES\) documentation for more information\. +Call Stack \(most recent call first\): + cmake_install\.cmake:[0-9]+ \(exec_get_runtime_dependencies\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt new file mode 100644 index 0000000..1692348 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-all-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Multiple conflicting paths found for librpath\.so: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath1/librpath\.so + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict-build/root-all/lib/rpath2/librpath\.so$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake new file mode 100644 index 0000000..f719499 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-conflict.cmake @@ -0,0 +1,54 @@ +enable_language(C) + +set(test1_names rpath) +set(test2_names rpath) + +file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n") +add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c") +install(TARGETS rpath DESTINATION lib/rpath1) +install(TARGETS rpath DESTINATION lib/rpath2) + +file(REMOVE "${CMAKE_BINARY_DIR}/test1.c") +add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n") + +target_link_libraries(test1 PRIVATE ${test1_names}) +set_property(TARGET test1 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath1" + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/test2.c") +add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n") + +target_link_libraries(test2 PRIVATE ${test2_names}) +set_property(TARGET test2 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath2" + ) + +install(TARGETS test1 test2 DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>" + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>" + PRE_INCLUDE_REGEXES "^librpath\\.so$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt new file mode 100644 index 0000000..83a87c9 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-all-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Failed to run objdump on: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile-build/root-all/bin/\.\./lib/libtest\.so$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake new file mode 100644 index 0000000..6567438 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-notfile.cmake @@ -0,0 +1,30 @@ +enable_language(C) +cmake_policy(SET CMP0095 NEW) + +file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void); + +int main(void) +{ + test(); + return 0; +} +]]) + +add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c") +add_executable(exe "${CMAKE_BINARY_DIR}/main.c") +target_link_libraries(exe PRIVATE test) +set_property(TARGET exe PROPERTY INSTALL_RPATH "\${ORIGIN}/../lib") + +install(TARGETS exe DESTINATION bin) + +install(CODE [[ + file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>") + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>" + PRE_INCLUDE_REGEXES "^libtest\\.so$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt new file mode 100644 index 0000000..eaca512 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved-all-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Could not resolve file libunresolved\.so$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake new file mode 100644 index 0000000..3efa305 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux-unresolved.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n unresolved();\n}\n") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n") +add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c") +target_link_libraries(testlib PRIVATE unresolved) +install(TARGETS testlib DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES "^libunresolved\\.so$" + PRE_EXCLUDE_REGEXES ".*" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake new file mode 100644 index 0000000..bd0f9f1 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux.cmake @@ -0,0 +1,169 @@ +enable_language(C) +cmake_policy(SET CMP0095 NEW) + +set(test_rpath_names + preexcluded + rpath_postexcluded + rpath + rpath_parent_postexcluded + rpath_parent + rpath_origin_postexcluded + rpath_origin + rpath_search_postexcluded + rpath_search + rpath_unresolved + conflict + ) +set(test_runpath_names + runpath_postexcluded + runpath + runpath_origin_postexcluded + runpath_origin + runpath_parent_unresolved + runpath_search_postexcluded + runpath_search + runpath_unresolved + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/test_rpath.c") +add_library(test_rpath SHARED "${CMAKE_BINARY_DIR}/test_rpath.c") +foreach(name ${test_rpath_names}) + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n") + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + + file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "void test_rpath(void)\n{\n") +foreach(name ${test_rpath_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_rpath.c" "}\n") + +install(TARGETS rpath_postexcluded DESTINATION lib/rpath_postexcluded) +install(TARGETS rpath DESTINATION lib/rpath) +install(TARGETS rpath_origin_postexcluded DESTINATION lib/rpath_origin_postexcluded) +install(TARGETS rpath_origin DESTINATION lib/rpath_origin) +install(TARGETS rpath_parent_postexcluded DESTINATION lib/rpath_parent_postexcluded) +install(TARGETS rpath rpath_origin rpath_parent DESTINATION lib/rpath_parent) +install(TARGETS rpath_search_postexcluded DESTINATION lib/rpath_search_postexcluded) +install(TARGETS rpath rpath_origin rpath_parent rpath_search DESTINATION lib/rpath_search) +install(TARGETS conflict DESTINATION lib/conflict) + +target_link_libraries(test_rpath PRIVATE ${test_rpath_names}) +set_property(TARGET test_rpath PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/lib/rpath" + "\$ORIGIN/rpath_origin_postexcluded" + "\${ORIGIN}/rpath_origin" + "${CMAKE_BINARY_DIR}/root-all/lib/conflict" + ) +target_link_options(test_rpath PRIVATE -Wl,--disable-new-dtags) + +file(REMOVE "${CMAKE_BINARY_DIR}/test_runpath.c") +add_library(test_runpath SHARED "${CMAKE_BINARY_DIR}/test_runpath.c") +foreach(name ${test_runpath_names} rpath conflict) + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n") + if(NOT name MATCHES "^(rpath|conflict)$") + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + endif() + + file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "void test_runpath(void)\n{\n") +foreach(name ${test_runpath_names} rpath conflict) + file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test_runpath.c" "}\n") + +install(TARGETS runpath_postexcluded DESTINATION lib/runpath_postexcluded) +install(TARGETS runpath DESTINATION lib/runpath) +install(TARGETS runpath_origin_postexcluded DESTINATION lib/runpath_origin_postexcluded) +install(TARGETS runpath_origin DESTINATION lib/runpath_origin) +install(TARGETS runpath_parent_unresolved DESTINATION lib/runpath_parent_unresolved) +install(TARGETS runpath_search_postexcluded DESTINATION lib/runpath_search_postexcluded) +install(TARGETS runpath runpath_origin runpath_search DESTINATION lib/runpath_search) +install(TARGETS conflict DESTINATION lib/conflict2) + +target_link_libraries(test_runpath PRIVATE ${test_runpath_names} rpath conflict) +set_property(TARGET test_runpath PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/runpath/../rpath" # Ensure that files that don't conflict are treated correctly + "${CMAKE_BINARY_DIR}/root-all/lib/runpath_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/lib/runpath" + "\${ORIGIN}/runpath_origin_postexcluded" + "\$ORIGIN/runpath_origin" + "${CMAKE_BINARY_DIR}/root-all/lib/conflict2" + ) +target_link_options(test_runpath PRIVATE -Wl,--enable-new-dtags) + +set_property(TARGET test_rpath ${test_rpath_names} test_runpath ${test_runpath_names} PROPERTY LIBRARY_OUTPUT_DIRECTORY lib) +install(TARGETS test_rpath test_runpath DESTINATION lib) + +add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c) +add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c) +add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c) +target_link_libraries(topexe PRIVATE test_rpath test_runpath) +target_link_libraries(toplib PRIVATE test_rpath test_runpath) +target_link_libraries(topmod PRIVATE test_rpath test_runpath) +set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib" + "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/lib/rpath_parent" + "${CMAKE_BINARY_DIR}/root-all/lib/runpath_parent_unresolved" + ) +target_link_options(topexe PRIVATE -Wl,--disable-new-dtags) +target_link_options(toplib PRIVATE -Wl,--disable-new-dtags) +target_link_options(topmod PRIVATE -Wl,--disable-new-dtags) + +install(TARGETS topexe toplib RUNTIME DESTINATION bin LIBRARY DESTINATION lib) +install(TARGETS topmod LIBRARY DESTINATION lib/modules) + +install(CODE [[ + function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile) + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR deps + UNRESOLVED_DEPENDENCIES_VAR udeps + CONFLICTING_DEPENDENCIES_PREFIX cdeps + PRE_INCLUDE_REGEXES + "^lib(test_rpath|rpath_postexcluded|rpath|rpath_parent_postexcluded|rpath_parent|rpath_origin_postexcluded|rpath_origin|rpath_search_postexcluded|rpath_search|rpath_unresolved|test_runpath|runpath_postexcluded|runpath|runpath_origin_postexcluded|runpath_origin|runpath_parent_unresolved|runpath_search_postexcluded|runpath_search|runpath_unresolved|conflict)\\.so$" + "^libc\\.so" + PRE_EXCLUDE_REGEXES ".*" + POST_INCLUDE_REGEXES "^.*/(libtest_rpath|rpath/librpath|rpath_parent/librpath_parent|rpath_search/librpath_search|libtest_runpath|runpath/librunpath|runpath_origin_postexcluded|runpath_origin|runpath_search/librunpath_search|conflict2?/libconflict)\\.so$" + POST_EXCLUDE_REGEXES ".*" + DIRECTORIES + "${CMAKE_INSTALL_PREFIX}/lib/rpath_search_postexcluded" + "${CMAKE_INSTALL_PREFIX}/lib/rpath_search" + "${CMAKE_INSTALL_PREFIX}/lib/runpath_search_postexcluded" + "${CMAKE_INSTALL_PREFIX}/lib/runpath_search" + ${ARGN} + ) + list(SORT deps) + list(SORT udeps) + list(SORT cdeps_FILENAMES) + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "") + foreach(cdep IN LISTS cdeps_FILENAMES) + set(cdep_values ${cdeps_${cdep}}) + list(SORT cdep_values) + file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n") + endforeach() + endfunction() + + exec_get_runtime_dependencies( + deps1.txt udeps1.txt cdeps1.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>" + ) + + exec_get_runtime_dependencies( + deps2.txt udeps2.txt cdeps2.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:toplib>" + ) + + exec_get_runtime_dependencies( + deps3.txt udeps3.txt cdeps3.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/lib/modules/$<TARGET_FILE_NAME:topmod>" + ) + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c new file mode 100644 index 0000000..d196afe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/topexe.c @@ -0,0 +1,9 @@ +extern void test_rpath(void); +extern void test_runpath(void); + +int main(void) +{ + test_rpath(); + test_runpath(); + return 0; +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c new file mode 100644 index 0000000..040e591 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-linux/toplib.c @@ -0,0 +1,8 @@ +extern void test_rpath(void); +extern void test_runpath(void); + +void toplib(void) +{ + test_rpath(); + test_runpath(); +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake new file mode 100644 index 0000000..4d6dde1 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-all-check.cmake @@ -0,0 +1,157 @@ +function(check_contents filename contents_regex) + if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}") + file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents) + if(NOT contents MATCHES "${contents_regex}") + string(APPEND RunCMake_TEST_FAILED "File contents: + ${contents} +do not match what we expected: + ${contents_regex} +in file: + ${CMAKE_INSTALL_PREFIX}/${filename}\n") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() + else() + string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() +endfunction() + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps1.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps1.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps2.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_executable_path_postexcluded\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps2.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps3.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@executable_path/../lib/executable_path_postexcluded/libexecutable_path_postexcluded\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_executable_path_postexcluded\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps3.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/executable_path/libexecutable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/bin/../lib/rpath_executable_path/librpath_executable_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps4.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps4.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps5.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps5.txt "^${_check}$") + +set(_check + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/bundle_executable/bin/../lib/executable_path_bundle/libexecutable_path_bundle\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/libtestlib\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/loader_path/libloader_path\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/../rpath/librpath\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/normal/libnormal\.dylib]] + [[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/rpath_loader_path/librpath_loader_path\.dylib]] + [[/usr/lib/libSystem\.B\.dylib]] + ) +check_contents(deps/deps6.txt "^${_check}$") + +set(_check + [[@executable_path/../lib/executable_path/libexecutable_path\.dylib]] + [[@loader_path/loader_path_unresolved/libloader_path_unresolved\.dylib]] + [[@rpath/librpath_executable_path\.dylib]] + [[@rpath/librpath_executable_path_bundle\.dylib]] + [[@rpath/librpath_loader_path_unresolved\.dylib]] + [[@rpath/librpath_unresolved\.dylib]] + ) +check_contents(deps/udeps6.txt "^${_check}$") + +set(_check + "^libconflict\\.dylib:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict/libconflict\\.dylib;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-build/root-all/executable/lib/conflict2/libconflict\\.dylib\n$" + ) +check_contents(deps/cdeps1.txt "${_check}") +check_contents(deps/cdeps2.txt "${_check}") +check_contents(deps/cdeps3.txt "${_check}") +check_contents(deps/cdeps4.txt "${_check}") +check_contents(deps/cdeps5.txt "${_check}") +check_contents(deps/cdeps6.txt "${_check}") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt new file mode 100644 index 0000000..bc9e97a --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-all-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Multiple conflicting paths found for librpath\.dylib: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath1/librpath\.dylib + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict-build/root-all/lib/rpath2/librpath\.dylib$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake new file mode 100644 index 0000000..a8446fe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-conflict.cmake @@ -0,0 +1,55 @@ +enable_language(C) + +set(test1_names rpath) +set(test2_names rpath) + +file(WRITE "${CMAKE_BINARY_DIR}/rpath.c" "void rpath(void) {}\n") +add_library(rpath SHARED "${CMAKE_BINARY_DIR}/rpath.c") +set_property(TARGET rpath PROPERTY INSTALL_NAME_DIR @rpath) +install(TARGETS rpath DESTINATION lib/rpath1) +install(TARGETS rpath DESTINATION lib/rpath2) + +file(REMOVE "${CMAKE_BINARY_DIR}/test1.c") +add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "void test1(void)\n{\n") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n") + +target_link_libraries(test1 PRIVATE ${test1_names}) +set_property(TARGET test1 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath1" + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/test2.c") +add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "void test2(void)\n{\n") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n") + +target_link_libraries(test2 PRIVATE ${test2_names}) +set_property(TARGET test2 PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/lib/rpath2" + ) + +install(TARGETS test1 test2 DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test1>" + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test2>" + PRE_INCLUDE_REGEXES "^@rpath/librpath\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt new file mode 100644 index 0000000..73ab9f1 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-all-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Failed to run otool on: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile-build/root-all/bin/\.\./lib/libtest\.dylib$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake new file mode 100644 index 0000000..3e4c434 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-notfile.cmake @@ -0,0 +1,30 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/test.c" "void test(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[extern void test(void); + +int main(void) +{ + test(); + return 0; +} +]]) + +add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c") +set_property(TARGET test PROPERTY INSTALL_NAME_DIR @rpath) +add_executable(exe "${CMAKE_BINARY_DIR}/main.c") +target_link_libraries(exe PRIVATE test) +set_property(TARGET exe PROPERTY INSTALL_RPATH "@loader_path/../lib") + +install(TARGETS exe DESTINATION bin) + +install(CODE [[ + file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:test>") + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>" + PRE_INCLUDE_REGEXES "^@rpath/libtest\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt new file mode 100644 index 0000000..01762b4 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved-all-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Could not resolve file @rpath/libunresolved\.dylib$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake new file mode 100644 index 0000000..c9b6c95 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos-unresolved.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "extern void unresolved(void);\nvoid testlib(void)\n{\n unresolved();\n}\n") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "void unresolved(void) {}\n") +add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c") +target_link_libraries(testlib PRIVATE unresolved) +install(TARGETS testlib DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES "^@rpath/libunresolved\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake new file mode 100644 index 0000000..6db05b3 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos.cmake @@ -0,0 +1,216 @@ +enable_language(C) + +set(testlib_names + preexcluded + executable_path + executable_path_bundle + executable_path_postexcluded + loader_path + loader_path_unresolved + loader_path_postexcluded + rpath + rpath_unresolved + rpath_postexcluded + rpath_executable_path + rpath_executable_path_bundle + rpath_executable_path_postexcluded + rpath_loader_path + rpath_loader_path_unresolved + rpath_loader_path_postexcluded + normal + normal_unresolved + normal_postexcluded + conflict + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +foreach(name ${testlib_names}) + if(name STREQUAL "normal") + file(WRITE "${CMAKE_BINARY_DIR}/normal.c" "extern void rpath(void);\nvoid normal(void)\n{\n rpath();\n}\n") + else() + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "void ${name}(void) {}\n") + endif() + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "void testlib(void)\n{\n") +foreach(name ${testlib_names}) + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n") +set_property(TARGET ${testlib_names} PROPERTY BUILD_WITH_INSTALL_NAME_DIR 1) +target_link_libraries(normal PRIVATE rpath) +set_property(TARGET normal PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/executable/lib/normal/../rpath" + ) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "extern void conflict(void);\nvoid testlib_conflict(void)\n{\n conflict();\n}\n") +add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c") +target_link_libraries(testlib_conflict PRIVATE conflict) + +set_property(TARGET testlib PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath" + "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_unresolved" + "${CMAKE_BINARY_DIR}/root-all/executable/lib/rpath_postexcluded" + "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict" + @executable_path/../lib/rpath_executable_path + @executable_path/../lib/rpath_executable_path_unresolved + @executable_path/../lib/rpath_executable_path_postexcluded + @loader_path/rpath_loader_path + @loader_path/rpath_loader_path_unresolved + @loader_path/rpath_loader_path_postexcluded + ) +set_property(TARGET testlib_conflict PROPERTY INSTALL_RPATH + "${CMAKE_BINARY_DIR}/root-all/executable/lib/conflict2" + ) + +foreach(t + executable_path + executable_path_postexcluded + loader_path + loader_path_postexcluded + rpath + rpath_postexcluded + rpath_executable_path + rpath_executable_path_postexcluded + rpath_loader_path + rpath_loader_path_postexcluded + conflict + ) + install(TARGETS ${t} DESTINATION executable/lib/${t}) +endforeach() +install(TARGETS conflict DESTINATION executable/lib/conflict2) + +foreach(t + executable_path_bundle + executable_path_postexcluded + loader_path_postexcluded + rpath_postexcluded + rpath_executable_path_bundle + rpath_executable_path_postexcluded + rpath_loader_path_postexcluded + ) + install(TARGETS ${t} DESTINATION bundle_executable/lib/${t}) +endforeach() + +foreach(t executable_path executable_path_bundle executable_path_postexcluded) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @executable_path/../lib/${t}) +endforeach() + +foreach(t loader_path loader_path_unresolved loader_path_postexcluded) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @loader_path/${t}) +endforeach() + +foreach(t + rpath + rpath_unresolved + rpath_postexcluded + rpath_executable_path + rpath_executable_path_bundle + rpath_executable_path_postexcluded + rpath_loader_path + rpath_loader_path_unresolved + rpath_loader_path_postexcluded + conflict + ) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR @rpath) +endforeach() + +foreach(t normal normal_unresolved normal_postexcluded) + set_property(TARGET ${t} PROPERTY INSTALL_NAME_DIR "${CMAKE_BINARY_DIR}/root-all/executable/lib/${t}") + if(NOT t STREQUAL "normal_unresolved") + install(TARGETS ${t} DESTINATION executable/lib/${t}) + endif() +endforeach() + +target_link_libraries(testlib PRIVATE ${testlib_names}) + +add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c) +add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c) +add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c) +target_link_libraries(topexe PRIVATE testlib) +target_link_libraries(toplib PRIVATE testlib) +target_link_libraries(topmod PRIVATE testlib) + +set_property(TARGET topexe toplib topmod PROPERTY INSTALL_RPATH "${CMAKE_BINARY_DIR}/root-all/executable/lib") + +install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION executable/bin LIBRARY DESTINATION executable/lib) +install(TARGETS topexe toplib topmod testlib testlib_conflict RUNTIME DESTINATION bundle_executable/bin LIBRARY DESTINATION bundle_executable/lib) + +install(CODE [[ + function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile) + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR deps + UNRESOLVED_DEPENDENCIES_VAR udeps + CONFLICTING_DEPENDENCIES_PREFIX cdeps + PRE_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|executable_path_postexcluded|loader_path|loader_path_unresolved|loader_path_postexcluded|rpath|rpath_unresolved|rpath_postexcluded|rpath_executable_path|rpath_executable_path_bundle|rpath_executable_path_postexcluded|rpath_loader_path|rpath_loader_path_unresolved|rpath_loader_path_postexcluded|normal|normal_unresolved|normal_postexcluded|conflict|System\\.B)\\.dylib$" + PRE_EXCLUDE_REGEXES ".*" + POST_INCLUDE_REGEXES "^.*/lib(testlib|executable_path|executable_path_bundle|loader_path|rpath|rpath_executable_path|rpath_executable_path_bundle|rpath_loader_path|normal|conflict|System\\.B)\\.dylib$" + POST_EXCLUDE_REGEXES ".*" + ${ARGN} + ) + list(SORT deps) + list(SORT udeps) + list(SORT cdeps_FILENAMES) + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "") + foreach(cdep IN LISTS cdeps_FILENAMES) + set(cdep_values ${cdeps_${cdep}}) + list(SORT cdep_values) + file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n") + endforeach() + endfunction() + + exec_get_runtime_dependencies( + deps1.txt udeps1.txt cdeps1.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + ) + + exec_get_runtime_dependencies( + deps2.txt udeps2.txt cdeps2.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>" + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + ) + + exec_get_runtime_dependencies( + deps3.txt udeps3.txt cdeps3.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + ) + + exec_get_runtime_dependencies( + deps4.txt udeps4.txt cdeps4.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/executable/bin/$<TARGET_FILE_NAME:topexe>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + BUNDLE_EXECUTABLE + "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>" + ) + + exec_get_runtime_dependencies( + deps5.txt udeps5.txt cdeps5.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:toplib>" + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>" + ) + + exec_get_runtime_dependencies( + deps6.txt udeps6.txt cdeps6.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:topmod>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/executable/lib/$<TARGET_FILE_NAME:testlib_conflict>" + BUNDLE_EXECUTABLE "${CMAKE_INSTALL_PREFIX}/bundle_executable/bin/$<TARGET_FILE_NAME:topexe>" + ) + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c new file mode 100644 index 0000000..20c6087 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/topexe.c @@ -0,0 +1,7 @@ +extern void testlib(void); + +int main(void) +{ + testlib(); + return 0; +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c new file mode 100644 index 0000000..cff1bff --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-macos/toplib.c @@ -0,0 +1,6 @@ +extern void testlib(void); + +void toplib(void) +{ + testlib(); +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt new file mode 100644 index 0000000..d506645 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project-stderr.txt @@ -0,0 +1,13 @@ +^CMake Warning \(dev\) at file-GET_RUNTIME_DEPENDENCIES-project\.cmake:[0-9]+ \(file\): + You have used file\(GET_RUNTIME_DEPENDENCIES\) in project mode\. This is + probably not what you intended to do\. Instead, please consider using it in + an install\(CODE\) or install\(SCRIPT\) command\. For example: + + install\(CODE \[\[ + file\(GET_RUNTIME_DEPENDENCIES + # \.\.\. + \) + ]]\) +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) +This warning is for project developers\. Use -Wno-dev to suppress it\.$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake new file mode 100644 index 0000000..842d7ab --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-project.cmake @@ -0,0 +1 @@ +file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt new file mode 100644 index 0000000..3db835c --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at file-GET_RUNTIME_DEPENDENCIES-unsupported\.cmake:[0-9]+ \(file\): + file GET_RUNTIME_DEPENDENCIES is not supported on system "[^ + ]+" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake new file mode 100644 index 0000000..b91eefe --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-unsupported.cmake @@ -0,0 +1,2 @@ +file(GET_RUNTIME_DEPENDENCIES RESOLVED_DEPENDENCIES_VAR deps) +message(FATAL_ERROR "This message should not be displayed") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake new file mode 100644 index 0000000..c120ce4 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-all-check.cmake @@ -0,0 +1,38 @@ +function(check_contents filename contents_regex) + if(EXISTS "${CMAKE_INSTALL_PREFIX}/${filename}") + file(READ "${CMAKE_INSTALL_PREFIX}/${filename}" contents) + if(NOT contents MATCHES "${contents_regex}") + string(APPEND RunCMake_TEST_FAILED "File contents: + ${contents} +do not match what we expected: + ${contents_regex} +in file: + ${CMAKE_INSTALL_PREFIX}/${filename}\n") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() + else() + string(APPEND RunCMake_TEST_FAILED "File ${CMAKE_INSTALL_PREFIX}/${filename} does not exist") + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) + endif() +endfunction() + +set(_check + [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.conflict/\.\./(lib)?libdir\.dll]=] + [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\.search/(lib)?search\.dll]=] + [=[[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?testlib\.dll]=] + ) +check_contents(deps/deps1.txt "^${_check}$") +check_contents(deps/deps2.txt "^${_check}$") +check_contents(deps/deps3.txt "^${_check}$") +set(_check + [=[(lib)?unresolved\.dll]=] + ) +check_contents(deps/udeps1.txt "^${_check}$") +check_contents(deps/udeps2.txt "^${_check}$") +check_contents(deps/udeps3.txt "^${_check}$") +set(_check + "^(lib)?conflict\\.dll:[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-build/root-all/bin/(lib)?conflict\\.dll\n$" + ) +check_contents(deps/cdeps1.txt "${_check}") +check_contents(deps/cdeps2.txt "${_check}") +check_contents(deps/cdeps3.txt "${_check}") diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt new file mode 100644 index 0000000..66ecb93 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-all-stderr.txt @@ -0,0 +1,7 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Multiple conflicting paths found for (lib)?path\.dll: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test1/(lib)?path\.dll + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict-build/root-all/lib/test2/(lib)?path\.dll$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake new file mode 100644 index 0000000..d413443 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-conflict.cmake @@ -0,0 +1,47 @@ +enable_language(C) + +set(test1_names path) +set(test2_names path) + +file(WRITE "${CMAKE_BINARY_DIR}/path.c" "__declspec(dllexport) void path(void) {}\n") +add_library(path SHARED "${CMAKE_BINARY_DIR}/path.c") + +file(REMOVE "${CMAKE_BINARY_DIR}/test1.c") +add_library(test1 SHARED "${CMAKE_BINARY_DIR}/test1.c") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllimport) extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "__declspec(dllexport) void test1(void)\n{\n") +foreach(name ${test1_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test1.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test1.c" "}\n") + +target_link_libraries(test1 PRIVATE ${test1_names}) + +file(REMOVE "${CMAKE_BINARY_DIR}/test2.c") +add_library(test2 SHARED "${CMAKE_BINARY_DIR}/test2.c") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllimport) extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "__declspec(dllexport) void test2(void)\n{\n") +foreach(name ${test2_names}) + file(APPEND "${CMAKE_BINARY_DIR}/test2.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/test2.c" "}\n") + +target_link_libraries(test2 PRIVATE ${test2_names}) + +install(TARGETS test1 path DESTINATION lib/test1) +install(TARGETS test2 path DESTINATION lib/test2) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/test1/$<TARGET_FILE_NAME:test1>" + "${CMAKE_INSTALL_PREFIX}/lib/test2/$<TARGET_FILE_NAME:test2>" + PRE_INCLUDE_REGEXES "^(lib)?path\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt new file mode 100644 index 0000000..f921409 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-all-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Failed to run (dumpbin|objdump) on: + + [^ +]*/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile-build/root-all/bin/(lib)?test\.dll$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake new file mode 100644 index 0000000..6665a3b --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-notfile.cmake @@ -0,0 +1,28 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/test.c" "__declspec(dllexport) void test(void) {}\n") +file(WRITE "${CMAKE_BINARY_DIR}/main.c" [[__declspec(dllimport) extern void test(void); + +int main(void) +{ + test(); + return 0; +} +]]) + +add_library(test SHARED "${CMAKE_BINARY_DIR}/test.c") +add_executable(exe "${CMAKE_BINARY_DIR}/main.c") +target_link_libraries(exe PRIVATE test) + +install(TARGETS exe DESTINATION bin) + +install(CODE [[ + file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:test>") + file(GET_RUNTIME_DEPENDENCIES + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:exe>" + PRE_INCLUDE_REGEXES "^(lib)?test\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt new file mode 100644 index 0000000..a20654c --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved-all-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error at cmake_install\.cmake:[0-9]+ \(file\): + file Could not resolve file (lib)?unresolved\.dll$ diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake new file mode 100644 index 0000000..4cc74c7 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows-unresolved.cmake @@ -0,0 +1,18 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void unresolved(void);\n__declspec(dllexport) void testlib(void)\n{\n unresolved();\n}\n") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +file(WRITE "${CMAKE_BINARY_DIR}/unresolved.c" "__declspec(dllexport) void unresolved(void) {}\n") +add_library(unresolved SHARED "${CMAKE_BINARY_DIR}/unresolved.c") +target_link_libraries(testlib PRIVATE unresolved) +install(TARGETS testlib DESTINATION lib) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES "^(lib)?unresolved\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$<TARGET_FILE_NAME:testlib>" + ) + message(FATAL_ERROR "This message should not be displayed") + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake new file mode 100644 index 0000000..19288d8 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows.cmake @@ -0,0 +1,114 @@ +enable_language(C) + +set(testlib_names + preexcluded + libdir_postexcluded + libdir + search_postexcluded + search + unresolved + conflict + ) + +file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c") +add_library(testlib SHARED "${CMAKE_BINARY_DIR}/testlib.c") +foreach(name ${testlib_names}) + file(WRITE "${CMAKE_BINARY_DIR}/${name}.c" "__declspec(dllexport) void ${name}(void) {}\n") + add_library(${name} SHARED "${CMAKE_BINARY_DIR}/${name}.c") + + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllimport) extern void ${name}(void);\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "__declspec(dllexport) void testlib(void)\n{\n") +foreach(name ${testlib_names}) + file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" " ${name}();\n") +endforeach() +file(APPEND "${CMAKE_BINARY_DIR}/testlib.c" "}\n") + +target_link_libraries(testlib PRIVATE ${testlib_names}) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib_conflict.c" "__declspec(dllimport) extern void conflict(void);\n__declspec(dllexport) void testlib_conflict(void)\n{\n conflict();\n}\n") +add_library(testlib_conflict SHARED "${CMAKE_BINARY_DIR}/testlib_conflict.c") +target_link_libraries(testlib_conflict PRIVATE conflict) + +file(WRITE "${CMAKE_BINARY_DIR}/testlib_noconflict.c" "__declspec(dllimport) extern void libdir(void);\n__declspec(dllexport) void testlib_noconflict(void)\n{\n libdir();\n}\n") +add_library(testlib_noconflict SHARED "${CMAKE_BINARY_DIR}/testlib_noconflict.c") +target_link_libraries(testlib_noconflict PRIVATE libdir) + +install(TARGETS testlib libdir_postexcluded libdir conflict testlib_noconflict DESTINATION bin) +install(TARGETS libdir search_postexcluded search DESTINATION bin/.search) # Prefixing with "." ensures it is the first item after list(SORT) +install(TARGETS testlib_conflict conflict DESTINATION bin/.conflict) + +add_executable(topexe file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c) +add_library(toplib SHARED file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c) +add_library(topmod MODULE file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c) +target_link_libraries(topexe PRIVATE testlib) +target_link_libraries(toplib PRIVATE testlib) +target_link_libraries(topmod PRIVATE testlib) + +install(TARGETS topexe toplib topmod DESTINATION bin) + +install(CODE [[ + function(exec_get_runtime_dependencies depsfile udepsfile cdepsfile) + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR deps + UNRESOLVED_DEPENDENCIES_VAR udeps + CONFLICTING_DEPENDENCIES_PREFIX cdeps + PRE_INCLUDE_REGEXES + "^(lib)?testlib\\.dll$" + "^(lib)?libdir_postexcluded\\.dll$" + "^(lib)?libdir\\.dll$" + "^(lib)?search_postexcluded\\.dll$" + "^(lib)?search\\.dll$" + "^(lib)?unresolved\\.dll$" + "^(lib)?conflict\\.dll$" + "^kernel32\\.dll$" + PRE_EXCLUDE_REGEXES ".*" + POST_INCLUDE_REGEXES + "^.*/(lib)?testlib\\.dll$" + "^.*/(lib)?libdir\\.dll$" + "^.*/(lib)?search\\.dll$" + "^.*/(lib)?conflict\\.dll$" + POST_EXCLUDE_REGEXES ".*" + DIRECTORIES + "${CMAKE_INSTALL_PREFIX}/bin/.search" + ${ARGN} + ) + list(SORT deps) + list(SORT udeps) + list(SORT cdeps_FILENAMES) + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${depsfile}" "${deps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${udepsfile}" "${udeps}") + file(WRITE "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "") + foreach(cdep IN LISTS cdeps_FILENAMES) + set(cdep_values ${cdeps_${cdep}}) + list(SORT cdep_values) + file(APPEND "${CMAKE_INSTALL_PREFIX}/deps/${cdepsfile}" "${cdep}:${cdep_values}\n") + endforeach() + endfunction() + + exec_get_runtime_dependencies( + deps1.txt udeps1.txt cdeps1.txt + EXECUTABLES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topexe>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>" + ) + + exec_get_runtime_dependencies( + deps2.txt udeps2.txt cdeps2.txt + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:toplib>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>" + ) + + exec_get_runtime_dependencies( + deps3.txt udeps3.txt cdeps3.txt + MODULES + "${CMAKE_INSTALL_PREFIX}/bin/$<TARGET_FILE_NAME:topmod>" + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/$<TARGET_FILE_NAME:testlib_conflict>" + "${CMAKE_INSTALL_PREFIX}/bin/.conflict/../$<TARGET_FILE_NAME:testlib_noconflict>" + ) + ]]) diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c new file mode 100644 index 0000000..713b8eb --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/topexe.c @@ -0,0 +1,7 @@ +__declspec(dllimport) extern void testlib(void); + +int main(void) +{ + testlib(); + return 0; +} diff --git a/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c new file mode 100644 index 0000000..6997175 --- /dev/null +++ b/Tests/RunCMake/install/file-GET_RUNTIME_DEPENDENCIES-windows/toplib.c @@ -0,0 +1,6 @@ +__declspec(dllimport) extern void testlib(void); + +__declspec(dllexport) void toplib(void) +{ + testlib(); +} diff --git a/Tests/Tutorial/Complete/tutorial.cxx b/Tests/Tutorial/Complete/tutorial.cxx index 443d195..4451cbd 100644 --- a/Tests/Tutorial/Complete/tutorial.cxx +++ b/Tests/Tutorial/Complete/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/MultiPackage/tutorial.cxx b/Tests/Tutorial/MultiPackage/tutorial.cxx index 443d195..4451cbd 100644 --- a/Tests/Tutorial/MultiPackage/tutorial.cxx +++ b/Tests/Tutorial/MultiPackage/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step10/tutorial.cxx b/Tests/Tutorial/Step10/tutorial.cxx index 443d195..4451cbd 100644 --- a/Tests/Tutorial/Step10/tutorial.cxx +++ b/Tests/Tutorial/Step10/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step11/tutorial.cxx b/Tests/Tutorial/Step11/tutorial.cxx index 3768855..38d4a79 100644 --- a/Tests/Tutorial/Step11/tutorial.cxx +++ b/Tests/Tutorial/Step11/tutorial.cxx @@ -10,7 +10,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step2/tutorial.cxx b/Tests/Tutorial/Step2/tutorial.cxx index 75b7d67..5ba34e8 100644 --- a/Tests/Tutorial/Step2/tutorial.cxx +++ b/Tests/Tutorial/Step2/tutorial.cxx @@ -9,7 +9,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step3/tutorial.cxx b/Tests/Tutorial/Step3/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step3/tutorial.cxx +++ b/Tests/Tutorial/Step3/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step4/tutorial.cxx b/Tests/Tutorial/Step4/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step4/tutorial.cxx +++ b/Tests/Tutorial/Step4/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step5/tutorial.cxx b/Tests/Tutorial/Step5/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step5/tutorial.cxx +++ b/Tests/Tutorial/Step5/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step6/tutorial.cxx b/Tests/Tutorial/Step6/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step6/tutorial.cxx +++ b/Tests/Tutorial/Step6/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step7/tutorial.cxx b/Tests/Tutorial/Step7/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step7/tutorial.cxx +++ b/Tests/Tutorial/Step7/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step8/tutorial.cxx b/Tests/Tutorial/Step8/tutorial.cxx index 1d5742d..c2b89df 100644 --- a/Tests/Tutorial/Step8/tutorial.cxx +++ b/Tests/Tutorial/Step8/tutorial.cxx @@ -13,7 +13,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Tests/Tutorial/Step9/tutorial.cxx b/Tests/Tutorial/Step9/tutorial.cxx index 73e67a9..6c41859 100644 --- a/Tests/Tutorial/Step9/tutorial.cxx +++ b/Tests/Tutorial/Step9/tutorial.cxx @@ -14,7 +14,7 @@ int main(int argc, char* argv[]) { if (argc < 2) { std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "." - << Tutorial_VERSION_MAJOR << std::endl; + << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0] << " number" << std::endl; return 1; } diff --git a/Utilities/cmlibuv/include/uv.h b/Utilities/cmlibuv/include/uv.h index e6dc736..eb80bfb 100644 --- a/Utilities/cmlibuv/include/uv.h +++ b/Utilities/cmlibuv/include/uv.h @@ -206,6 +206,7 @@ typedef enum { /* Handle types. */ typedef struct uv_loop_s uv_loop_t; typedef struct uv_handle_s uv_handle_t; +typedef struct uv_dir_s uv_dir_t; typedef struct uv_stream_s uv_stream_t; typedef struct uv_tcp_s uv_tcp_t; typedef struct uv_udp_s uv_udp_t; @@ -634,7 +635,11 @@ UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); UV_EXTERN int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags); +UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr); +UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen); UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen); @@ -1112,6 +1117,11 @@ typedef struct { } uv_timeval_t; typedef struct { + int64_t tv_sec; + int32_t tv_usec; +} uv_timeval64_t; + +typedef struct { uv_timeval_t ru_utime; /* user CPU time used */ uv_timeval_t ru_stime; /* system CPU time used */ uint64_t ru_maxrss; /* maximum resident set size */ @@ -1162,6 +1172,17 @@ UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size); UV_EXTERN int uv_os_setenv(const char* name, const char* value); UV_EXTERN int uv_os_unsetenv(const char* name); +#ifdef MAXHOSTNAMELEN +# define UV_MAXHOSTNAMESIZE (MAXHOSTNAMELEN + 1) +#else + /* + Fallback for the maximum hostname size, including the null terminator. The + Windows gethostname() documentation states that 256 bytes will always be + large enough to hold the null-terminated hostname. + */ +# define UV_MAXHOSTNAMESIZE 256 +#endif + UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size); UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); @@ -1199,9 +1220,19 @@ typedef enum { UV_FS_FCHOWN, UV_FS_REALPATH, UV_FS_COPYFILE, - UV_FS_LCHOWN + UV_FS_LCHOWN, + UV_FS_OPENDIR, + UV_FS_READDIR, + UV_FS_CLOSEDIR } uv_fs_type; +struct uv_dir_s { + uv_dirent_t* dirents; + size_t nentries; + void* reserved[4]; + UV_DIR_PRIVATE_FIELDS +}; + /* uv_fs_t is a subclass of uv_req_t. */ struct uv_fs_s { UV_REQ_FIELDS @@ -1294,6 +1325,18 @@ UV_EXTERN int uv_fs_scandir(uv_loop_t* loop, uv_fs_cb cb); UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent); +UV_EXTERN int uv_fs_opendir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb); +UV_EXTERN int uv_fs_readdir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb); +UV_EXTERN int uv_fs_closedir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb); UV_EXTERN int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1536,6 +1579,7 @@ UV_EXTERN int uv_chdir(const char* dir); UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); +UV_EXTERN uint64_t uv_get_constrained_memory(void); UV_EXTERN uint64_t uv_hrtime(void); @@ -1589,9 +1633,29 @@ UV_EXTERN void uv_key_delete(uv_key_t* key); UV_EXTERN void* uv_key_get(uv_key_t* key); UV_EXTERN void uv_key_set(uv_key_t* key, void* value); +UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv); + typedef void (*uv_thread_cb)(void* arg); UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg); + +typedef enum { + UV_THREAD_NO_FLAGS = 0x00, + UV_THREAD_HAS_STACK_SIZE = 0x01 +} uv_thread_create_flags; + +struct uv_thread_options_s { + unsigned int flags; + size_t stack_size; + /* More fields may be added at any time. */ +}; + +typedef struct uv_thread_options_s uv_thread_options_t; + +UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid, + const uv_thread_options_t* params, + uv_thread_cb entry, + void* arg); UV_EXTERN uv_thread_t uv_thread_self(void); UV_EXTERN int uv_thread_join(uv_thread_t *tid); UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2); diff --git a/Utilities/cmlibuv/include/uv/unix.h b/Utilities/cmlibuv/include/uv/unix.h index 3c1b363..011abcf 100644 --- a/Utilities/cmlibuv/include/uv/unix.h +++ b/Utilities/cmlibuv/include/uv/unix.h @@ -31,13 +31,14 @@ #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> -#include <netdb.h> +#include <netdb.h> /* MAXHOSTNAMELEN on Solaris */ #include <termios.h> #include <pwd.h> #if !defined(__MVS__) #include <semaphore.h> +#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */ #endif #include <pthread.h> #include <signal.h> @@ -50,8 +51,6 @@ # include "linux.h" #elif defined (__MVS__) # include "os390.h" -#elif defined(__PASE__) -# include "posix.h" #elif defined(_AIX) # include "aix.h" #elif defined(__sun) @@ -64,9 +63,12 @@ defined(__OpenBSD__) || \ defined(__NetBSD__) # include "bsd.h" -#elif defined(__CYGWIN__) || defined(__MSYS__) +#elif defined(__PASE__) || \ + defined(__CYGWIN__) || \ + defined(__MSYS__) || \ + defined(__GNU__) # include "posix.h" -#elif defined(__GNU__) +#elif defined(__HAIKU__) # include "posix.h" #endif @@ -149,7 +151,9 @@ typedef pthread_cond_t uv_cond_t; typedef pthread_key_t uv_key_t; /* Note: guard clauses should match uv_barrier_init's in src/unix/thread.c. */ -#if defined(_AIX) || !defined(PTHREAD_BARRIER_SERIAL_THREAD) +#if defined(_AIX) || \ + defined(__OpenBSD__) || \ + !defined(PTHREAD_BARRIER_SERIAL_THREAD) /* TODO(bnoordhuis) Merge into uv_barrier_t in v2. */ struct _uv_barrier { uv_mutex_t mutex; @@ -178,6 +182,9 @@ typedef uid_t uv_uid_t; typedef struct dirent uv__dirent_t; +#define UV_DIR_PRIVATE_FIELDS \ + DIR* dir; + #if defined(DT_UNKNOWN) # define HAVE_DIRENT_TYPES # if defined(DT_REG) diff --git a/Utilities/cmlibuv/include/uv/version.h b/Utilities/cmlibuv/include/uv/version.h index abc140a..97f0bc2 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 24 +#define UV_VERSION_MINOR 29 #define UV_VERSION_PATCH 2 #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 f3d3809..7f77cc2 100644 --- a/Utilities/cmlibuv/include/uv/win.h +++ b/Utilities/cmlibuv/include/uv/win.h @@ -312,6 +312,11 @@ typedef struct uv__dirent_s { char d_name[1]; } uv__dirent_t; +#define UV_DIR_PRIVATE_FIELDS \ + HANDLE dir_handle; \ + WIN32_FIND_DATAW find_data; \ + BOOL need_find_call; + #define HAVE_DIRENT_TYPES #define UV__DT_DIR UV_DIRENT_DIR #define UV__DT_FILE UV_DIRENT_FILE diff --git a/Utilities/cmlibuv/src/fs-poll.c b/Utilities/cmlibuv/src/fs-poll.c index 6c82dfc..89864e2 100644 --- a/Utilities/cmlibuv/src/fs-poll.c +++ b/Utilities/cmlibuv/src/fs-poll.c @@ -22,12 +22,20 @@ #include "uv.h" #include "uv-common.h" +#ifdef _WIN32 +#include "win/internal.h" +#include "win/handle-inl.h" +#define uv__make_close_pending(h) uv_want_endgame((h)->loop, (h)) +#else +#include "unix/internal.h" +#endif + #include <assert.h> #include <stdlib.h> #include <string.h> struct poll_ctx { - uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */ + uv_fs_poll_t* parent_handle; int busy_polling; unsigned int interval; uint64_t start_time; @@ -36,6 +44,7 @@ struct poll_ctx { uv_timer_t timer_handle; uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */ uv_stat_t statbuf; + struct poll_ctx* previous; /* context from previous start()..stop() period */ char path[1]; /* variable length */ }; @@ -49,6 +58,7 @@ static uv_stat_t zero_statbuf; int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL); + handle->poll_ctx = NULL; return 0; } @@ -62,7 +72,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle, size_t len; int err; - if (uv__is_active(handle)) + if (uv_is_active((uv_handle_t*)handle)) return 0; loop = handle->loop; @@ -90,6 +100,8 @@ int uv_fs_poll_start(uv_fs_poll_t* handle, if (err < 0) goto error; + if (handle->poll_ctx != NULL) + ctx->previous = handle->poll_ctx; handle->poll_ctx = ctx; uv__handle_start(handle); @@ -104,19 +116,17 @@ error: int uv_fs_poll_stop(uv_fs_poll_t* handle) { struct poll_ctx* ctx; - if (!uv__is_active(handle)) + if (!uv_is_active((uv_handle_t*)handle)) return 0; ctx = handle->poll_ctx; assert(ctx != NULL); - assert(ctx->parent_handle != NULL); - ctx->parent_handle = NULL; - handle->poll_ctx = NULL; + assert(ctx->parent_handle == handle); /* Close the timer if it's active. If it's inactive, there's a stat request * in progress and poll_cb will take care of the cleanup. */ - if (uv__is_active(&ctx->timer_handle)) + if (uv_is_active((uv_handle_t*)&ctx->timer_handle)) uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); uv__handle_stop(handle); @@ -129,7 +139,7 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) { struct poll_ctx* ctx; size_t required_len; - if (!uv__is_active(handle)) { + if (!uv_is_active((uv_handle_t*)handle)) { *size = 0; return UV_EINVAL; } @@ -153,6 +163,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) { void uv__fs_poll_close(uv_fs_poll_t* handle) { uv_fs_poll_stop(handle); + + if (handle->poll_ctx == NULL) + uv__make_close_pending((uv_handle_t*)handle); } @@ -173,14 +186,13 @@ static void poll_cb(uv_fs_t* req) { uv_stat_t* statbuf; struct poll_ctx* ctx; uint64_t interval; + uv_fs_poll_t* handle; ctx = container_of(req, struct poll_ctx, fs_req); + handle = ctx->parent_handle; - if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */ - uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); - uv_fs_req_cleanup(req); - return; - } + if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) + goto out; if (req->result != 0) { if (ctx->busy_polling != req->result) { @@ -205,7 +217,7 @@ static void poll_cb(uv_fs_t* req) { out: uv_fs_req_cleanup(req); - if (ctx->parent_handle == NULL) { /* handle has been stopped by callback */ + if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) { uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); return; } @@ -219,8 +231,27 @@ out: } -static void timer_close_cb(uv_handle_t* handle) { - uv__free(container_of(handle, struct poll_ctx, timer_handle)); +static void timer_close_cb(uv_handle_t* timer) { + struct poll_ctx* ctx; + struct poll_ctx* it; + struct poll_ctx* last; + uv_fs_poll_t* handle; + + ctx = container_of(timer, struct poll_ctx, timer_handle); + handle = ctx->parent_handle; + if (ctx == handle->poll_ctx) { + handle->poll_ctx = ctx->previous; + if (handle->poll_ctx == NULL && uv__is_closing(handle)) + uv__make_close_pending((uv_handle_t*)handle); + } else { + for (last = handle->poll_ctx, it = last->previous; + it != ctx; + last = it, it = it->previous) { + assert(last->previous != NULL); + } + last->previous = ctx->previous; + } + uv__free(ctx); } diff --git a/Utilities/cmlibuv/src/threadpool.c b/Utilities/cmlibuv/src/threadpool.c index 4258933..7aa5755 100644 --- a/Utilities/cmlibuv/src/threadpool.c +++ b/Utilities/cmlibuv/src/threadpool.c @@ -27,7 +27,7 @@ #include <stdlib.h> -#define MAX_THREADPOOL_SIZE 128 +#define MAX_THREADPOOL_SIZE 1024 static uv_once_t once = UV_ONCE_INIT; static uv_cond_t cond; diff --git a/Utilities/cmlibuv/src/unix/aix.c b/Utilities/cmlibuv/src/unix/aix.c index 337e58e..1f36926 100644 --- a/Utilities/cmlibuv/src/unix/aix.c +++ b/Utilities/cmlibuv/src/unix/aix.c @@ -344,6 +344,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { perfstat_cpu_total_t ps_total; int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); @@ -1041,6 +1046,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct poll_ctl pc; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct pollfd*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; diff --git a/Utilities/cmlibuv/src/unix/async.c b/Utilities/cmlibuv/src/unix/async.c index 0b450ae..a5c47bc 100644 --- a/Utilities/cmlibuv/src/unix/async.c +++ b/Utilities/cmlibuv/src/unix/async.c @@ -61,14 +61,43 @@ int uv_async_send(uv_async_t* handle) { if (ACCESS_ONCE(int, handle->pending) != 0) return 0; - if (cmpxchgi(&handle->pending, 0, 1) == 0) - uv__async_send(handle->loop); + /* Tell the other thread we're busy with the handle. */ + if (cmpxchgi(&handle->pending, 0, 1) != 0) + return 0; + + /* Wake up the other thread's event loop. */ + uv__async_send(handle->loop); + + /* Tell the other thread we're done. */ + if (cmpxchgi(&handle->pending, 1, 2) != 1) + abort(); return 0; } +/* Only call this from the event loop thread. */ +static int uv__async_spin(uv_async_t* handle) { + int rc; + + for (;;) { + /* rc=0 -- handle is not pending. + * rc=1 -- handle is pending, other thread is still working with it. + * rc=2 -- handle is pending, other thread is done. + */ + rc = cmpxchgi(&handle->pending, 2, 0); + + if (rc != 1) + return rc; + + /* Other thread is busy with this handle, spin until it's done. */ + cpu_relax(); + } +} + + void uv__async_close(uv_async_t* handle) { + uv__async_spin(handle); QUEUE_REMOVE(&handle->queue); uv__handle_stop(handle); } @@ -109,8 +138,8 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->async_handles, q); - if (cmpxchgi(&h->pending, 1, 0) == 0) - continue; + if (0 == uv__async_spin(h)) + continue; /* Not pending. */ if (h->async_cb == NULL) continue; diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h index be741dc..995aca6 100644 --- a/Utilities/cmlibuv/src/unix/atomic-ops.h +++ b/Utilities/cmlibuv/src/unix/atomic-ops.h @@ -23,7 +23,6 @@ #endif UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); -UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)); UV_UNUSED(static void cpu_relax(void)); /* Prefer hand-rolled assembly over the gcc builtins because the latter also @@ -49,43 +48,7 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { 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 -} - -UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) { -#if defined(__i386__) || defined(__x86_64__) - long out; - __asm__ __volatile__ ("lock; cmpxchg %2, %1;" - : "=a" (out), "+m" (*(volatile long*) ptr) - : "r" (newval), "0" (oldval) - : "memory"); - return out; -#elif defined(_AIX) && (defined(__xlC__) || defined(__ibmxl__)) - const long out = (*(volatile int*) ptr); -# if defined(__64BIT__) - __compare_and_swaplp(ptr, &oldval, newval); -# else - __compare_and_swap(ptr, &oldval, newval); -# endif /* if defined(__64BIT__) */ - return out; -#elif defined (__MVS__) -#ifdef _LP64 - unsigned long long op4; - if (__plo_CSSTGR(ptr, (unsigned long long*) &oldval, newval, - (unsigned long long*) ptr, *ptr, &op4)) -#else - unsigned long op4; - if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, - (unsigned int*) ptr, *ptr, &op4)) -#endif - return oldval; - else - return op4; -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) - return atomic_cas_ulong(ptr, oldval, newval); + return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)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 3c2253f..0d7bbe6 100644 --- a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c +++ b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c @@ -31,6 +31,10 @@ #include <net/if_dl.h> #endif +#if defined(__HAIKU__) +#define IFF_RUNNING IFF_LINK +#endif + static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) return 1; @@ -45,7 +49,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (exclude_type == UV__EXCLUDE_IFPHYS) return (ent->ifa_addr->sa_family != AF_LINK); #endif -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__HAIKU__) /* * On BSD getifaddrs returns information related to the raw underlying * devices. We're not interested in this information. @@ -84,7 +89,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { return 0; } - *addresses = uv__malloc(*count * sizeof(**addresses)); + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); if (*addresses == NULL) { freeifaddrs(addrs); @@ -116,6 +122,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address++; } +#if !(defined(__CYGWIN__) || defined(__MSYS__)) /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) @@ -124,20 +131,15 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address = *addresses; for (i = 0; i < *count; i++) { -#if defined(__CYGWIN__) || defined(__MSYS__) - memset(address->phys_addr, 0, sizeof(address->phys_addr)); -#else if (strcmp(address->name, ent->ifa_name) == 0) { struct sockaddr_dl* sa_addr; sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); - } else { - memset(address->phys_addr, 0, sizeof(address->phys_addr)); } -#endif address++; } } +#endif freeifaddrs(addrs); diff --git a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c index 309ec79..d42ff05 100644 --- a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c +++ b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c @@ -137,4 +137,13 @@ int uv__utimesat(int dirfd, const char* path, const struct timespec times[2], errno = ENOSYS; return -1; } + +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf) { + errno = ENOSYS; + return -1; +} #endif diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c index a8d6adb..93df7af 100644 --- a/Utilities/cmlibuv/src/unix/core.c +++ b/Utilities/cmlibuv/src/unix/core.c @@ -42,9 +42,9 @@ #include <pwd.h> #include <sched.h> #include <sys/utsname.h> +#include <sys/time.h> #ifdef __sun -# include <netdb.h> /* MAXHOSTNAMELEN on Solaris */ # include <sys/filio.h> # include <sys/types.h> # include <sys/wait.h> @@ -91,13 +91,8 @@ #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 +#if defined(__linux__) +#include <sys/syscall.h> #endif static int uv__run_pending(uv_loop_t* loop); @@ -174,7 +169,9 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { case UV_FS_POLL: uv__fs_poll_close((uv_fs_poll_t*)handle); - break; + /* Poll handles use file system requests, and one of them may still be + * running. The poll code will call uv__make_close_pending() for us. */ + return; case UV_SIGNAL: uv__signal_close((uv_signal_t*) handle); @@ -520,6 +517,34 @@ skip: } +/* close() on macos has the "interesting" quirk that it fails with EINTR + * without closing the file descriptor when a thread is in the cancel state. + * That's why libuv calls close$NOCANCEL() instead. + * + * glibc on linux has a similar issue: close() is a cancellation point and + * will unwind the thread when it's in the cancel state. Work around that + * by making the system call directly. Musl libc is unaffected. + */ +int uv__close_nocancel(int fd) { +#if defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" +#if defined(__LP64__) + extern int close$NOCANCEL(int); + return close$NOCANCEL(fd); +#else + extern int close$NOCANCEL$UNIX2003(int); + return close$NOCANCEL$UNIX2003(fd); +#endif +#pragma GCC diagnostic pop +#elif defined(__linux__) + return syscall(SYS_close, fd); +#else + return close(fd); +#endif +} + + int uv__close_nocheckstdio(int fd) { int saved_errno; int rc; @@ -527,7 +552,7 @@ int uv__close_nocheckstdio(int fd) { assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */ saved_errno = errno; - rc = close(fd); + rc = uv__close_nocancel(fd); if (rc == -1) { rc = UV__ERR(errno); if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS)) @@ -562,7 +587,7 @@ int uv__nonblock_ioctl(int fd, int set) { } -#if !defined(__CYGWIN__) && !defined(__MSYS__) +#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__) int uv__cloexec_ioctl(int fd, int set) { int r; @@ -895,7 +920,8 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) { QUEUE_REMOVE(&w->pending_queue); /* Remove stale events for this file descriptor */ - uv__platform_invalidate_fd(loop, w->fd); + if (w->fd != -1) + uv__platform_invalidate_fd(loop, w->fd); } @@ -929,7 +955,7 @@ int uv_getrusage(uv_rusage_t* rusage) { rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec; rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec; -#if !defined(__MVS__) +#if !defined(__MVS__) && !defined(__HAIKU__) rusage->ru_maxrss = usage.ru_maxrss; rusage->ru_ixrss = usage.ru_ixrss; rusage->ru_idrss = usage.ru_idrss; @@ -1294,7 +1320,7 @@ int uv_os_gethostname(char* buffer, size_t* size) { instead by creating a large enough buffer and comparing the hostname length to the size input. */ - char buf[MAXHOSTNAMELEN + 1]; + char buf[UV_MAXHOSTNAMESIZE]; size_t len; if (buffer == NULL || size == NULL || *size == 0) @@ -1426,3 +1452,39 @@ error: buffer->machine[0] = '\0'; return r; } + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen) { + socklen_t socklen; + uv_os_fd_t fd; + int r; + + r = uv_fileno(handle, &fd); + if (r < 0) + return r; + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t) *namelen; + + if (func(fd, name, &socklen)) + return UV__ERR(errno); + + *namelen = (int) socklen; + return 0; +} + +int uv_gettimeofday(uv_timeval64_t* tv) { + struct timeval time; + + if (tv == NULL) + return UV_EINVAL; + + if (gettimeofday(&time, NULL) != 0) + return UV__ERR(errno); + + tv->tv_sec = (int64_t) time.tv_sec; + tv->tv_usec = (int32_t) time.tv_usec; + return 0; +} diff --git a/Utilities/cmlibuv/src/unix/darwin.c b/Utilities/cmlibuv/src/unix/darwin.c index 31ad8a9..e4cd8ff 100644 --- a/Utilities/cmlibuv/src/unix/darwin.c +++ b/Utilities/cmlibuv/src/unix/darwin.c @@ -117,6 +117,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/Utilities/cmlibuv/src/unix/freebsd.c b/Utilities/cmlibuv/src/unix/freebsd.c index 0f729cf..7de88d6 100644 --- a/Utilities/cmlibuv/src/unix/freebsd.c +++ b/Utilities/cmlibuv/src/unix/freebsd.c @@ -137,6 +137,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c index bffc956..3023b1e 100644 --- a/Utilities/cmlibuv/src/unix/fs.c +++ b/Utilities/cmlibuv/src/unix/fs.c @@ -47,7 +47,7 @@ #if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel_) || \ + defined(__FreeBSD_kernel__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # define HAVE_PREADV 1 @@ -60,7 +60,6 @@ #endif #if defined(__APPLE__) -# include <copyfile.h> # include <sys/sysctl.h> #elif defined(__linux__) && !defined(FICLONE) # include <sys/ioctl.h> @@ -143,19 +142,34 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */ while (0) +static int uv__fs_close(int fd) { + int rc; + + rc = uv__close_nocancel(fd); + if (rc == -1) + if (errno == EINTR || errno == EINPROGRESS) + rc = 0; /* The close is in progress, not an error. */ + + return rc; +} + + 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. If F_FULLFSYNC is not - * supported by the file system we should fall back to fsync(). This is the - * same approach taken by sqlite. + * supported by the file system we fall back to F_BARRIERFSYNC or fsync(). + * This is the same approach taken by sqlite, except sqlite does not issue + * an F_BARRIERFSYNC call. */ int r; r = fcntl(req->file, F_FULLFSYNC); if (r != 0) + r = fcntl(req->file, 85 /* F_BARRIERFSYNC */); /* fsync + barrier */ + if (r != 0) r = fsync(req->file); return r; #else @@ -178,7 +192,8 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) { static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ - || defined(_AIX71) + || defined(_AIX71) \ + || defined(__HAIKU__) /* utimesat() has nanosecond resolution but we stick to microseconds * for the sake of consistency with other platforms. */ @@ -327,6 +342,18 @@ done: req->bufs = NULL; req->nbufs = 0; +#ifdef __PASE__ + /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */ + if (result == -1 && errno == EOPNOTSUPP) { + struct stat buf; + ssize_t rc; + rc = fstat(req->file, &buf); + if (rc == 0 && S_ISDIR(buf.st_mode)) { + errno = EISDIR; + } + } +#endif + return result; } @@ -349,7 +376,7 @@ static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) { static ssize_t uv__fs_scandir(uv_fs_t* req) { - uv__dirent_t **dents; + uv__dirent_t** dents; int n; dents = NULL; @@ -373,6 +400,87 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) { return n; } +static int uv__fs_opendir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = uv__malloc(sizeof(*dir)); + if (dir == NULL) + goto error; + + dir->dir = opendir(req->path); + if (dir->dir == NULL) + goto error; + + req->ptr = dir; + return 0; + +error: + uv__free(dir); + req->ptr = NULL; + return -1; +} + +static int uv__fs_readdir(uv_fs_t* req) { + uv_dir_t* dir; + uv_dirent_t* dirent; + struct dirent* res; + unsigned int dirent_idx; + unsigned int i; + + dir = req->ptr; + dirent_idx = 0; + + while (dirent_idx < dir->nentries) { + /* readdir() returns NULL on end of directory, as well as on error. errno + is used to differentiate between the two conditions. */ + errno = 0; + res = readdir(dir->dir); + + if (res == NULL) { + if (errno != 0) + goto error; + break; + } + + if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0) + continue; + + dirent = &dir->dirents[dirent_idx]; + dirent->name = uv__strdup(res->d_name); + + if (dirent->name == NULL) + goto error; + + dirent->type = uv__fs_get_dirent_type(res); + ++dirent_idx; + } + + return dirent_idx; + +error: + for (i = 0; i < dirent_idx; ++i) { + uv__free((char*) dir->dirents[i].name); + dir->dirents[i].name = NULL; + } + + return -1; +} + +static int uv__fs_closedir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = req->ptr; + + if (dir->dir != NULL) { + closedir(dir->dir); + dir->dir = NULL; + } + + uv__free(req->ptr); + req->ptr = NULL; + return 0; +} + #if defined(_POSIX_PATH_MAX) # define UV__FS_PATH_MAX _POSIX_PATH_MAX #elif defined(PATH_MAX) @@ -702,7 +810,8 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { static ssize_t uv__fs_utime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) \ - || defined(__sun) + || defined(__sun) \ + || defined(__HAIKU__) /* utimesat() has nanosecond resolution but we stick to microseconds * for the sake of consistency with other platforms. */ @@ -806,45 +915,6 @@ done: } static ssize_t uv__fs_copyfile(uv_fs_t* req) { -#if defined(__APPLE__) && !TARGET_OS_IPHONE - /* On macOS, use the native copyfile(3). */ - static int can_clone; - copyfile_flags_t flags; - char buf[64]; - size_t len; - int major; - - flags = COPYFILE_ALL; - - if (req->flags & UV_FS_COPYFILE_EXCL) - flags |= COPYFILE_EXCL; - - /* Check OS version. Cloning is only supported on macOS >= 10.12. */ - if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { - if (can_clone == 0) { - len = sizeof(buf); - if (sysctlbyname("kern.osrelease", buf, &len, NULL, 0)) - return UV__ERR(errno); - - if (1 != sscanf(buf, "%d", &major)) - abort(); - - can_clone = -1 + 2 * (major >= 16); /* macOS >= 10.12 */ - } - - if (can_clone < 0) - return UV_ENOSYS; - } - - /* copyfile() simply ignores COPYFILE_CLONE if it's not supported. */ - if (req->flags & UV_FS_COPYFILE_FICLONE) - flags |= 1 << 24; /* COPYFILE_CLONE */ - - if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) - flags |= 1 << 25; /* COPYFILE_CLONE_FORCE */ - - return copyfile(req->path, req->new_path, NULL, flags); -#else uv_fs_t fs_req; uv_file srcfd; uv_file dstfd; @@ -971,7 +1041,6 @@ out: errno = UV__ERR(result); return -1; -#endif } static void uv__to_stat(struct stat* src, uv_stat_t* dst) { @@ -1051,10 +1120,84 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { } +static int uv__fs_statx(int fd, + const char* path, + int is_fstat, + int is_lstat, + uv_stat_t* buf) { + STATIC_ASSERT(UV_ENOSYS != -1); +#ifdef __linux__ + static int no_statx; + struct uv__statx statxbuf; + int dirfd; + int flags; + int mode; + int rc; + + if (no_statx) + return UV_ENOSYS; + + dirfd = AT_FDCWD; + flags = 0; /* AT_STATX_SYNC_AS_STAT */ + mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */ + + if (is_fstat) { + dirfd = fd; + flags |= 0x1000; /* AT_EMPTY_PATH */ + } + + if (is_lstat) + flags |= AT_SYMLINK_NOFOLLOW; + + rc = uv__statx(dirfd, path, flags, mode, &statxbuf); + + if (rc == -1) { + /* EPERM happens when a seccomp filter rejects the system call. + * Has been observed with libseccomp < 2.3.3 and docker < 18.04. + */ + if (errno != EINVAL && errno != EPERM && errno != ENOSYS) + return -1; + + no_statx = 1; + return UV_ENOSYS; + } + + buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor; + buf->st_mode = statxbuf.stx_mode; + buf->st_nlink = statxbuf.stx_nlink; + buf->st_uid = statxbuf.stx_uid; + buf->st_gid = statxbuf.stx_gid; + buf->st_rdev = statxbuf.stx_rdev_major; + buf->st_ino = statxbuf.stx_ino; + buf->st_size = statxbuf.stx_size; + buf->st_blksize = statxbuf.stx_blksize; + buf->st_blocks = statxbuf.stx_blocks; + buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec; + buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec; + buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec; + buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec; + buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec; + buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec; + buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec; + buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec; + buf->st_flags = 0; + buf->st_gen = 0; + + return 0; +#else + return UV_ENOSYS; +#endif /* __linux__ */ +} + + static int uv__fs_stat(const char *path, uv_stat_t *buf) { struct stat pbuf; int ret; + ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf); + if (ret != UV_ENOSYS) + return ret; + ret = stat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1067,6 +1210,10 @@ static int uv__fs_lstat(const char *path, uv_stat_t *buf) { struct stat pbuf; int ret; + ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf); + if (ret != UV_ENOSYS) + return ret; + ret = lstat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1079,6 +1226,10 @@ static int uv__fs_fstat(int fd, uv_stat_t *buf) { struct stat pbuf; int ret; + ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf); + if (ret != UV_ENOSYS) + return ret; + ret = fstat(fd, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1167,7 +1318,7 @@ static void uv__fs_work(struct uv__work* w) { X(ACCESS, access(req->path, req->flags)); X(CHMOD, chmod(req->path, req->mode)); X(CHOWN, chown(req->path, req->uid, req->gid)); - X(CLOSE, close(req->file)); + X(CLOSE, uv__fs_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)); @@ -1184,6 +1335,9 @@ static void uv__fs_work(struct uv__work* w) { X(OPEN, uv__fs_open(req)); X(READ, uv__fs_read(req)); X(SCANDIR, uv__fs_scandir(req)); + X(OPENDIR, uv__fs_opendir(req)); + X(READDIR, uv__fs_readdir(req)); + X(CLOSEDIR, uv__fs_closedir(req)); X(READLINK, uv__fs_readlink(req)); X(REALPATH, uv__fs_realpath(req)); X(RENAME, rename(req->path, req->new_path)); @@ -1454,6 +1608,40 @@ int uv_fs_scandir(uv_loop_t* loop, POST; } +int uv_fs_opendir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + INIT(OPENDIR); + PATH; + POST; +} + +int uv_fs_readdir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(READDIR); + + if (dir == NULL || dir->dir == NULL || dir->dirents == NULL) + return UV_EINVAL; + + req->ptr = dir; + POST; +} + +int uv_fs_closedir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(CLOSEDIR); + + if (dir == NULL) + return UV_EINVAL; + + req->ptr = dir; + POST; +} int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, @@ -1594,6 +1782,9 @@ void uv_fs_req_cleanup(uv_fs_t* req) { req->path = NULL; req->new_path = NULL; + if (req->fs_type == UV_FS_READDIR && req->ptr != NULL) + uv__fs_readdir_cleanup(req); + if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) uv__fs_scandir_cleanup(req); @@ -1601,7 +1792,7 @@ void uv_fs_req_cleanup(uv_fs_t* req) { uv__free(req->bufs); req->bufs = NULL; - if (req->ptr != &req->statbuf) + if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf) uv__free(req->ptr); req->ptr = NULL; } diff --git a/Utilities/cmlibuv/src/unix/fsevents.c b/Utilities/cmlibuv/src/unix/fsevents.c index c430562..ddacda3 100644 --- a/Utilities/cmlibuv/src/unix/fsevents.c +++ b/Utilities/cmlibuv/src/unix/fsevents.c @@ -21,9 +21,10 @@ #include "uv.h" #include "internal.h" -#if TARGET_OS_IPHONE +#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */ +/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */ int uv__fsevents_init(uv_fs_event_t* handle) { return 0; diff --git a/Utilities/cmlibuv/src/unix/getaddrinfo.c b/Utilities/cmlibuv/src/unix/getaddrinfo.c index 6d23fbe..d7ca7d1 100644 --- a/Utilities/cmlibuv/src/unix/getaddrinfo.c +++ b/Utilities/cmlibuv/src/unix/getaddrinfo.c @@ -92,7 +92,9 @@ int uv__getaddrinfo_translate_error(int sys_err) { } assert(!"unknown EAI_* error code"); abort(); +#ifndef __SUNPRO_C return 0; /* Pacify compiler. */ +#endif } diff --git a/Utilities/cmlibuv/src/unix/haiku.c b/Utilities/cmlibuv/src/unix/haiku.c new file mode 100644 index 0000000..7708851 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/haiku.c @@ -0,0 +1,176 @@ +/* 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 <FindDirectory.h> /* find_path() */ +#include <OS.h> + + +void uv_loadavg(double avg[3]) { + avg[0] = 0; + avg[1] = 0; + avg[2] = 0; +} + + +int uv_exepath(char* buffer, size_t* size) { + char abspath[B_PATH_NAME_LENGTH]; + status_t status; + ssize_t abspath_len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + status = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, abspath, + sizeof(abspath)); + if (status != B_OK) + return UV__ERR(status); + + abspath_len = uv__strscpy(buffer, abspath, *size); + *size -= 1; + if (abspath_len >= 0 && *size > (size_t)abspath_len) + *size = (size_t)abspath_len; + + return 0; +} + + +uint64_t uv_get_free_memory(void) { + status_t status; + system_info sinfo; + + status = get_system_info(&sinfo); + if (status != B_OK) + return 0; + + return (sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE; +} + + +uint64_t uv_get_total_memory(void) { + status_t status; + system_info sinfo; + + status = get_system_info(&sinfo); + if (status != B_OK) + return 0; + + return sinfo.max_pages * B_PAGE_SIZE; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +int uv_resident_set_memory(size_t* rss) { + area_info area; + ssize_t cookie; + status_t status; + thread_info thread; + + status = get_thread_info(find_thread(NULL), &thread); + if (status != B_OK) + return UV__ERR(status); + + cookie = 0; + *rss = 0; + while (get_next_area_info(thread.team, &cookie, &area) == B_OK) + *rss += area.ram_size; + + return 0; +} + + +int uv_uptime(double* uptime) { + /* system_time() returns time since booting in microseconds */ + *uptime = (double)system_time() / 1000000; + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + cpu_topology_node_info* topology_infos; + int i; + status_t status; + system_info system; + uint32_t topology_count; + uint64_t cpuspeed; + uv_cpu_info_t* cpu_info; + + if (cpu_infos == NULL || count == NULL) + return UV_EINVAL; + + status = get_cpu_topology_info(NULL, &topology_count); + if (status != B_OK) + return UV__ERR(status); + + topology_infos = uv__malloc(topology_count * sizeof(*topology_infos)); + if (topology_infos == NULL) + return UV_ENOMEM; + + status = get_cpu_topology_info(topology_infos, &topology_count); + if (status != B_OK) { + uv__free(topology_infos); + return UV__ERR(status); + } + + cpuspeed = 0; + for (i = 0; i < (int)topology_count; i++) { + if (topology_infos[i].type == B_TOPOLOGY_CORE) { + cpuspeed = topology_infos[i].data.core.default_frequency; + break; + } + } + + uv__free(topology_infos); + + status = get_system_info(&system); + if (status != B_OK) + return UV__ERR(status); + + *cpu_infos = uv__calloc(system.cpu_count, sizeof(**cpu_infos)); + if (*cpu_infos == NULL) + return UV_ENOMEM; + + /* CPU time and model are not exposed by Haiku. */ + cpu_info = *cpu_infos; + for (i = 0; i < (int)system.cpu_count; i++) { + cpu_info->model = uv__strdup("unknown"); + cpu_info->speed = (int)(cpuspeed / 1000000); + cpu_info++; + } + *count = system.cpu_count; + + return 0; +} + +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); +} diff --git a/Utilities/cmlibuv/src/unix/ibmi.c b/Utilities/cmlibuv/src/unix/ibmi.c index 13fed6c..c7e1051 100644 --- a/Utilities/cmlibuv/src/unix/ibmi.c +++ b/Utilities/cmlibuv/src/unix/ibmi.c @@ -55,19 +55,155 @@ #include <strings.h> #include <sys/vnode.h> +#include <as400_protos.h> + + +typedef struct { + int bytes_available; + int bytes_returned; + char current_date_and_time[8]; + char system_name[8]; + char elapsed_time[6]; + char restricted_state_flag; + char reserved; + int percent_processing_unit_used; + int jobs_in_system; + int percent_permanent_addresses; + int percent_temporary_addresses; + int system_asp; + int percent_system_asp_used; + int total_auxiliary_storage; + int current_unprotected_storage_used; + int maximum_unprotected_storage_used; + int percent_db_capability; + int main_storage_size; + int number_of_partitions; + int partition_identifier; + int reserved1; + int current_processing_capacity; + char processor_sharing_attribute; + char reserved2[3]; + int number_of_processors; + int active_jobs_in_system; + int active_threads_in_system; + int maximum_jobs_in_system; + int percent_temporary_256mb_segments_used; + int percent_temporary_4gb_segments_used; + int percent_permanent_256mb_segments_used; + int percent_permanent_4gb_segments_used; + int percent_current_interactive_performance; + int percent_uncapped_cpu_capacity_used; + int percent_shared_processor_pool_used; + long main_storage_size_long; +} SSTS0200; + + +static int get_ibmi_system_status(SSTS0200* rcvr) { + /* rcvrlen is input parameter 2 to QWCRSSTS */ + unsigned int rcvrlen = sizeof(*rcvr); + + /* format is input parameter 3 to QWCRSSTS ("SSTS0200" in EBCDIC) */ + unsigned char format[] = {0xE2, 0xE2, 0xE3, 0xE2, 0xF0, 0xF2, 0xF0, 0xF0}; + + /* reset_status is input parameter 4 to QWCRSSTS ("*NO " in EBCDIC) */ + unsigned char reset_status[] = { + 0x5C, 0xD5, 0xD6, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 + }; + + /* errcode is input parameter 5 to QWCRSSTS */ + struct _errcode { + int bytes_provided; + int bytes_available; + char msgid[7]; + } errcode; + + /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */ + ILEpointer __attribute__((aligned(16))) qwcrssts_pointer; + + /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */ + void* qwcrssts_argv[6]; + + /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */ + int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS"); + + if (rc != 0) + return rc; + + /* initialize the QWCRSSTS returned info structure */ + memset(rcvr, 0, sizeof(*rcvr)); + + /* initialize the QWCRSSTS error code structure */ + memset(&errcode, 0, sizeof(errcode)); + errcode.bytes_provided = sizeof(errcode); + + /* initialize the array of argument pointers for the QWCRSSTS API */ + qwcrssts_argv[0] = rcvr; + qwcrssts_argv[1] = &rcvrlen; + qwcrssts_argv[2] = &format; + qwcrssts_argv[3] = &reset_status; + qwcrssts_argv[4] = &errcode; + qwcrssts_argv[5] = NULL; + + /* Call the IBM i QWCRSSTS API from PASE */ + rc = _PGMCALL(&qwcrssts_pointer, (void**)&qwcrssts_argv, 0); + + return rc; +} + + uint64_t uv_get_free_memory(void) { - return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + /* The amount of main storage, in kilobytes, in the system. */ + uint64_t main_storage_size = rcvr.main_storage_size; + + /* The current amount of storage in use for temporary objects. + * in millions (M) of bytes. + */ + uint64_t current_unprotected_storage_used = + rcvr.current_unprotected_storage_used * 1024ULL; + + uint64_t free_storage_size = + (main_storage_size - current_unprotected_storage_used) * 1024ULL; + + return free_storage_size < 0 ? 0 : free_storage_size; } uint64_t uv_get_total_memory(void) { - return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + return (uint64_t)rcvr.main_storage_size * 1024ULL; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ } void uv_loadavg(double avg[3]) { + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) { avg[0] = avg[1] = avg[2] = 0; return; + } + + /* The average (in tenths) of the elapsed time during which the processing + * units were in use. For example, a value of 411 in binary would be 41.1%. + * This percentage could be greater than 100% for an uncapped partition. + */ + double processing_unit_used_percent = + rcvr.percent_processing_unit_used / 1000.0; + + avg[0] = avg[1] = avg[2] = processing_unit_used_percent; } @@ -111,3 +247,4 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return 0; } + diff --git a/Utilities/cmlibuv/src/unix/internal.h b/Utilities/cmlibuv/src/unix/internal.h index 48fe6e8..b43c0b1 100644 --- a/Utilities/cmlibuv/src/unix/internal.h +++ b/Utilities/cmlibuv/src/unix/internal.h @@ -105,8 +105,7 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); */ #if defined(__clang__) || \ defined(__GNUC__) || \ - defined(__INTEL_COMPILER) || \ - defined(__SUNPRO_C) + defined(__INTEL_COMPILER) # define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration # define UV_UNUSED(declaration) __attribute__((unused)) declaration #else @@ -194,6 +193,7 @@ int uv__nonblock_ioctl(int fd, int set); int uv__nonblock_fcntl(int fd, int set); int uv__close(int fd); /* preserves errno */ int uv__close_nocheckstdio(int fd); +int uv__close_nocancel(int fd); int uv__socket(int domain, int type, int protocol); ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); void uv__make_close_pending(uv_handle_t* handle); @@ -316,4 +316,11 @@ UV_UNUSED(static char* uv__basename_r(const char* path)) { int uv__inotify_fork(uv_loop_t* loop, void* old_watchers); #endif +typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen); + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/kqueue.c b/Utilities/cmlibuv/src/unix/kqueue.c index c24f96e..c04e7a4 100644 --- a/Utilities/cmlibuv/src/unix/kqueue.c +++ b/Utilities/cmlibuv/src/unix/kqueue.c @@ -59,7 +59,7 @@ int uv__kqueue_init(uv_loop_t* loop) { } -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 static int uv__has_forked_with_cfrunloop; #endif @@ -70,7 +70,7 @@ int uv__io_fork(uv_loop_t* loop) { if (err) return err; -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if (loop->cf_state != NULL) { /* We cannot start another CFRunloop and/or thread in the child process; CF aborts if you try or if you try to touch the thread @@ -86,7 +86,7 @@ int uv__io_fork(uv_loop_t* loop) { uv__free(loop->cf_state); loop->cf_state = NULL; } -#endif +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ return err; } @@ -387,6 +387,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct kevent*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; @@ -457,7 +458,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (uv__is_active(handle)) return UV_EINVAL; -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 /* Nullify field to perform checks later */ handle->cf_cb = NULL; handle->realpath = NULL; @@ -481,7 +482,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, } return r; } -#endif /* defined(__APPLE__) */ +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ /* TODO open asynchronously - but how do we report back errors? */ fd = open(path, O_RDONLY); @@ -489,8 +490,11 @@ int uv_fs_event_start(uv_fs_event_t* handle, return UV__ERR(errno); handle->path = uv__strdup(path); - if (handle->path == NULL) + if (handle->path == NULL) { + uv__close_nocheckstdio(fd); return UV_ENOMEM; + } + handle->cb = cb; uv__handle_start(handle); uv__io_init(&handle->event_watcher, uv__fs_event, fd); @@ -509,7 +513,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { uv__handle_stop(handle); -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if (!uv__has_forked_with_cfrunloop) r = uv__fsevents_close(handle); #endif diff --git a/Utilities/cmlibuv/src/unix/linux-core.c b/Utilities/cmlibuv/src/unix/linux-core.c index 3341b94..b539beb 100644 --- a/Utilities/cmlibuv/src/unix/linux-core.c +++ b/Utilities/cmlibuv/src/unix/linux-core.c @@ -26,6 +26,7 @@ #include "uv.h" #include "internal.h" +#include <inttypes.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -79,16 +80,20 @@ static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci); static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); -static unsigned long read_cpufreq(unsigned int cpunum); +static uint64_t read_cpufreq(unsigned int cpunum); int uv__platform_loop_init(uv_loop_t* loop) { int fd; - fd = epoll_create1(EPOLL_CLOEXEC); + /* It was reported that EPOLL_CLOEXEC is not defined on Android API < 21, + * a.k.a. Lollipop. Since EPOLL_CLOEXEC is an alias for O_CLOEXEC on all + * architectures, we just use that instead. + */ + fd = epoll_create1(O_CLOEXEC); /* epoll_create1() can fail either because it's not implemented (old kernel) - * or because it doesn't understand the EPOLL_CLOEXEC flag. + * or because it doesn't understand the O_CLOEXEC flag. */ if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { fd = epoll_create(256); @@ -141,6 +146,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct epoll_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; @@ -714,20 +720,20 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci) { - unsigned long clock_ticks; struct uv_cpu_times_s ts; - unsigned long user; - unsigned long nice; - unsigned long sys; - unsigned long idle; - unsigned long dummy; - unsigned long irq; - unsigned int num; - unsigned int len; + uint64_t clock_ticks; + uint64_t user; + uint64_t nice; + uint64_t sys; + uint64_t idle; + uint64_t dummy; + uint64_t irq; + uint64_t num; + uint64_t len; char buf[1024]; clock_ticks = sysconf(_SC_CLK_TCK); - assert(clock_ticks != (unsigned long) -1); + assert(clock_ticks != (uint64_t) -1); assert(clock_ticks != 0); rewind(statfile_fp); @@ -760,7 +766,8 @@ static int read_times(FILE* statfile_fp, * fields, they're not allowed in C89 mode. */ if (6 != sscanf(buf + len, - "%lu %lu %lu %lu %lu %lu", + "%" PRIu64 " %" PRIu64 " %" PRIu64 + "%" PRIu64 " %" PRIu64 " %" PRIu64, &user, &nice, &sys, @@ -782,8 +789,8 @@ static int read_times(FILE* statfile_fp, } -static unsigned long read_cpufreq(unsigned int cpunum) { - unsigned long val; +static uint64_t read_cpufreq(unsigned int cpunum) { + uint64_t val; char buf[1024]; FILE* fp; @@ -796,7 +803,7 @@ static unsigned long read_cpufreq(unsigned int cpunum) { if (fp == NULL) return 0; - if (fscanf(fp, "%lu", &val) != 1) + if (fscanf(fp, "%" PRIu64, &val) != 1) val = 0; fclose(fp); @@ -859,7 +866,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { return 0; } - *addresses = uv__malloc(*count * sizeof(**addresses)); + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return UV_ENOMEM; @@ -898,11 +906,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address = *addresses; for (i = 0; i < (*count); i++) { - if (strcmp(address->name, ent->ifa_name) == 0) { + size_t namelen = strlen(ent->ifa_name); + /* Alias interface share the same physical address */ + if (strncmp(address->name, ent->ifa_name, namelen) == 0 && + (address->name[namelen] == 0 || address->name[namelen] == ':')) { sll = (struct sockaddr_ll*)ent->ifa_addr; memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); - } else { - memset(address->phys_addr, 0, sizeof(address->phys_addr)); } address++; } @@ -932,3 +941,114 @@ void uv__set_process_title(const char* title) { prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ #endif } + + +static uint64_t uv__read_proc_meminfo(const char* what) { + uint64_t rc; + ssize_t n; + char* p; + int fd; + char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ + + rc = 0; + fd = uv__open_cloexec("/proc/meminfo", O_RDONLY); + + if (fd == -1) + return 0; + + n = read(fd, buf, sizeof(buf) - 1); + + if (n <= 0) + goto out; + + buf[n] = '\0'; + p = strstr(buf, what); + + if (p == NULL) + goto out; + + p += strlen(what); + + if (1 != sscanf(p, "%" PRIu64 " kB", &rc)) + goto out; + + rc *= 1024; + +out: + + if (uv__close_nocheckstdio(fd)) + abort(); + + return rc; +} + + +uint64_t uv_get_free_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemFree:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.freeram * info.mem_unit; + + return 0; +} + + +uint64_t uv_get_total_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemTotal:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.totalram * info.mem_unit; + + return 0; +} + + +static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) { + char filename[256]; + uint64_t rc; + int fd; + ssize_t n; + char buf[32]; /* Large enough to hold an encoded uint64_t. */ + + snprintf(filename, 256, "/sys/fs/cgroup/%s/%s", cgroup, param); + + rc = 0; + fd = uv__open_cloexec(filename, O_RDONLY); + + if (fd < 0) + return 0; + + n = read(fd, buf, sizeof(buf) - 1); + + if (n > 0) { + buf[n] = '\0'; + sscanf(buf, "%" PRIu64, &rc); + } + + if (uv__close_nocheckstdio(fd)) + abort(); + + return rc; +} + + +uint64_t uv_get_constrained_memory(void) { + /* + * This might return 0 if there was a problem getting the memory limit from + * cgroups. This is OK because a return value of 0 signifies that the memory + * limit is unknown. + */ + return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes"); +} diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.c b/Utilities/cmlibuv/src/unix/linux-syscalls.c index bfd7544..5637cf9 100644 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.c +++ b/Utilities/cmlibuv/src/unix/linux-syscalls.c @@ -187,6 +187,21 @@ # endif #endif /* __NR_pwritev */ +#ifndef __NR_statx +# if defined(__x86_64__) +# define __NR_statx 332 +# elif defined(__i386__) +# define __NR_statx 383 +# elif defined(__aarch64__) +# define __NR_statx 397 +# elif defined(__arm__) +# define __NR_statx (UV_SYSCALL_BASE + 397) +# elif defined(__ppc__) +# define __NR_statx 383 +# elif defined(__s390__) +# define __NR_statx 379 +# endif +#endif /* __NR_statx */ int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) { #if defined(__i386__) @@ -336,3 +351,19 @@ int uv__dup3(int oldfd, int newfd, int flags) { return errno = ENOSYS, -1; #endif } + + +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf) { + /* __NR_statx make Android box killed by SIGSYS. + * That looks like a seccomp2 sandbox filter rejecting the system call. + */ +#if defined(__NR_statx) && !defined(__ANDROID__) + return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); +#else + return errno = ENOSYS, -1; +#endif +} diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.h b/Utilities/cmlibuv/src/unix/linux-syscalls.h index 3dfd329..7e58bfa 100644 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.h +++ b/Utilities/cmlibuv/src/unix/linux-syscalls.h @@ -80,6 +80,36 @@ #define UV__IN_DELETE_SELF 0x400 #define UV__IN_MOVE_SELF 0x800 +struct uv__statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t unused0; +}; + +struct uv__statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t unused0; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct uv__statx_timestamp stx_atime; + struct uv__statx_timestamp stx_btime; + struct uv__statx_timestamp stx_ctime; + struct uv__statx_timestamp stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t unused1[14]; +}; + struct uv__inotify_event { int32_t wd; uint32_t mask; @@ -113,5 +143,10 @@ int uv__sendmmsg(int fd, ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); int uv__dup3(int oldfd, int newfd, int flags); +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf); #endif /* UV_LINUX_SYSCALL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/netbsd.c b/Utilities/cmlibuv/src/unix/netbsd.c index a2a4e52..c649bb3 100644 --- a/Utilities/cmlibuv/src/unix/netbsd.c +++ b/Utilities/cmlibuv/src/unix/netbsd.c @@ -126,6 +126,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc2 *kinfo = NULL; diff --git a/Utilities/cmlibuv/src/unix/openbsd.c b/Utilities/cmlibuv/src/unix/openbsd.c index bffb58b..ffae768 100644 --- a/Utilities/cmlibuv/src/unix/openbsd.c +++ b/Utilities/cmlibuv/src/unix/openbsd.c @@ -136,6 +136,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { struct kinfo_proc kinfo; size_t page_size = getpagesize(); diff --git a/Utilities/cmlibuv/src/unix/os390.c b/Utilities/cmlibuv/src/unix/os390.c index dc146e3..273ded7 100644 --- a/Utilities/cmlibuv/src/unix/os390.c +++ b/Utilities/cmlibuv/src/unix/os390.c @@ -356,6 +356,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { char* ascb; char* rax; @@ -657,6 +662,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct epoll_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; diff --git a/Utilities/cmlibuv/src/unix/pipe.c b/Utilities/cmlibuv/src/unix/pipe.c index 9657bc9..7d97550 100644 --- a/Utilities/cmlibuv/src/unix/pipe.c +++ b/Utilities/cmlibuv/src/unix/pipe.c @@ -213,7 +213,7 @@ void uv_pipe_connect(uv_connect_t* req, } if (err == 0) - uv__io_start(handle->loop, &handle->io_watcher, POLLIN | POLLOUT); + uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); out: handle->delayed_error = err; @@ -231,9 +231,6 @@ out: } -typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); - - static int uv__pipe_getsockpeername(const uv_pipe_t* handle, uv__peersockfunc func, char* buffer, @@ -244,10 +241,13 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, addrlen = sizeof(sa); memset(&sa, 0, addrlen); - err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen); + err = uv__getsockpeername((const uv_handle_t*) handle, + func, + (struct sockaddr*) &sa, + (int*) &addrlen); if (err < 0) { *size = 0; - return UV__ERR(errno); + return err; } #if defined(__linux__) diff --git a/Utilities/cmlibuv/src/unix/posix-poll.c b/Utilities/cmlibuv/src/unix/posix-poll.c index f3181f9..a3b9f21 100644 --- a/Utilities/cmlibuv/src/unix/posix-poll.c +++ b/Utilities/cmlibuv/src/unix/posix-poll.c @@ -298,6 +298,8 @@ update_timeout: void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { size_t i; + assert(fd >= 0); + if (loop->poll_fds_iterating) { /* uv__io_poll is currently iterating. Just invalidate fd. */ for (i = 0; i < loop->poll_fds_used; i++) diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c index e9579f5..f4826bf 100644 --- a/Utilities/cmlibuv/src/unix/process.c +++ b/Utilities/cmlibuv/src/unix/process.c @@ -426,6 +426,11 @@ static void uv__process_child_init(const uv_process_options_t* options, if (n == SIGKILL || n == SIGSTOP) continue; /* Can't be changed. */ +#if defined(__HAIKU__) + if (n == SIGKILLTHR) + continue; /* Can't be changed. */ +#endif + if (SIG_ERR != signal(n, SIG_DFL)) continue; @@ -486,6 +491,8 @@ int uv_spawn(uv_loop_t* loop, UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | + UV_PROCESS_WINDOWS_HIDE_CONSOLE | + UV_PROCESS_WINDOWS_HIDE_GUI | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); diff --git a/Utilities/cmlibuv/src/unix/stream.c b/Utilities/cmlibuv/src/unix/stream.c index 4b9123f..8121f64 100644 --- a/Utilities/cmlibuv/src/unix/stream.c +++ b/Utilities/cmlibuv/src/unix/stream.c @@ -745,13 +745,13 @@ static int uv__write_req_update(uv_stream_t* stream, buf = req->bufs + req->write_index; - while (n > 0) { + do { len = n < buf->len ? n : buf->len; buf->base += len; buf->len -= len; buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */ n -= len; - } + } while (n > 0); req->write_index = buf - req->bufs; @@ -897,7 +897,7 @@ start: goto error; } - if (n > 0 && uv__write_req_update(stream, req, n)) { + if (n >= 0 && uv__write_req_update(stream, req, n)) { uv__write_req_finish(req); return; /* TODO(bnoordhuis) Start trying to write the next request. */ } @@ -1541,7 +1541,7 @@ int uv_try_write(uv_stream_t* stream, } if (written == 0 && req_size != 0) - return UV_EAGAIN; + return req.error < 0 ? req.error : UV_EAGAIN; else return written; } diff --git a/Utilities/cmlibuv/src/unix/sunos.c b/Utilities/cmlibuv/src/unix/sunos.c index aac6504..0cd25c1 100644 --- a/Utilities/cmlibuv/src/unix/sunos.c +++ b/Utilities/cmlibuv/src/unix/sunos.c @@ -121,6 +121,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct port_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; @@ -138,8 +139,10 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0)) return UV__ERR(errno); - if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) + if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) { + perror("(libuv) port_dissociate()"); abort(); + } return 0; } @@ -177,8 +180,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w = QUEUE_DATA(q, uv__io_t, watcher_queue); assert(w->pevents != 0); - if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0)) + if (port_associate(loop->backend_fd, + PORT_SOURCE_FD, + w->fd, + w->pevents, + 0)) { + perror("(libuv) port_associate()"); abort(); + } w->events = w->pevents; } @@ -222,10 +231,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* Work around another kernel bug: port_getn() may return events even * on error. */ - if (errno == EINTR || errno == ETIME) + if (errno == EINTR || errno == ETIME) { saved_errno = errno; - else + } else { + perror("(libuv) port_getn()"); abort(); + } } /* Update loop->time unconditionally. It's tempting to skip the update when @@ -373,6 +384,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { (void) getloadavg(avg, 3); } diff --git a/Utilities/cmlibuv/src/unix/tcp.c b/Utilities/cmlibuv/src/unix/tcp.c index 2982851..8cedcd6 100644 --- a/Utilities/cmlibuv/src/unix/tcp.c +++ b/Utilities/cmlibuv/src/unix/tcp.c @@ -82,7 +82,7 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { handle->flags |= flags; return 0; } - + /* Query to see if tcp socket is bound. */ slen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); @@ -235,12 +235,16 @@ int uv__tcp_connect(uv_connect_t* req, if (r == -1 && errno != 0) { if (errno == EINPROGRESS) ; /* not an error */ - else if (errno == ECONNREFUSED) - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. + else if (errno == ECONNREFUSED +#if defined(__OpenBSD__) + || errno == EINVAL +#endif + ) + /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the + * next tick to report the error. Solaris and OpenBSD wants to report + * immediately -- other unixes want to wait. */ - handle->delayed_error = UV__ERR(errno); + handle->delayed_error = UV__ERR(ECONNREFUSED); else return UV__ERR(errno); } @@ -279,44 +283,28 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - socklen_t socklen; if (handle->delayed_error) return handle->delayed_error; - if (uv__stream_fd(handle) < 0) - return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ - - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; - - if (getsockname(uv__stream_fd(handle), name, &socklen)) - return UV__ERR(errno); - - *namelen = (int) socklen; - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen); } int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - socklen_t socklen; if (handle->delayed_error) return handle->delayed_error; - if (uv__stream_fd(handle) < 0) - return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ - - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; - - if (getpeername(uv__stream_fd(handle), name, &socklen)) - return UV__ERR(errno); - - *namelen = (int) socklen; - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen); } diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c index 2900470..cd0b7aa 100644 --- a/Utilities/cmlibuv/src/unix/thread.c +++ b/Utilities/cmlibuv/src/unix/thread.c @@ -48,8 +48,10 @@ STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t)); #endif -/* Note: guard clauses should match uv_barrier_t's in include/uv/uv-unix.h. */ -#if defined(_AIX) || !defined(PTHREAD_BARRIER_SERIAL_THREAD) +/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */ +#if defined(_AIX) || \ + defined(__OpenBSD__) || \ + !defined(PTHREAD_BARRIER_SERIAL_THREAD) int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { struct _uv_barrier* b; int rc; @@ -176,8 +178,21 @@ static size_t thread_stack_size(void) { 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) - return lim.rlim_cur; + + /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is + * too small to safely receive signals on. + * + * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has + * the largest MINSIGSTKSZ of the architectures that musl supports) so + * let's use that as a lower bound. + * + * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ + * is between 28 and 133 KB when compiling against glibc, depending + * on the architecture. + */ + if (lim.rlim_cur >= 8192) + if (lim.rlim_cur >= PTHREAD_STACK_MIN) + return lim.rlim_cur; } #endif @@ -192,13 +207,36 @@ static size_t thread_stack_size(void) { int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { + uv_thread_options_t params; + params.flags = UV_THREAD_NO_FLAGS; + return uv_thread_create_ex(tid, ¶ms, entry, arg); +} + +int uv_thread_create_ex(uv_thread_t* tid, + const uv_thread_options_t* params, + void (*entry)(void *arg), + void *arg) { int err; - size_t stack_size; pthread_attr_t* attr; pthread_attr_t attr_storage; + size_t pagesize; + size_t stack_size; + + stack_size = + params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0; attr = NULL; - stack_size = thread_stack_size(); + if (stack_size == 0) { + stack_size = thread_stack_size(); + } else { + pagesize = (size_t)getpagesize(); + /* Round up to the nearest page boundary. */ + stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); +#ifdef PTHREAD_STACK_MIN + if (stack_size < PTHREAD_STACK_MIN) + stack_size = PTHREAD_STACK_MIN; +#endif + } if (stack_size > 0) { attr = &attr_storage; @@ -778,7 +816,9 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { return UV_ETIMEDOUT; abort(); +#ifndef __SUNPRO_C return UV_EINVAL; /* Satisfy the compiler. */ +#endif } diff --git a/Utilities/cmlibuv/src/unix/udp.c b/Utilities/cmlibuv/src/unix/udp.c index ec337ec..b578e7b 100644 --- a/Utilities/cmlibuv/src/unix/udp.c +++ b/Utilities/cmlibuv/src/unix/udp.c @@ -30,6 +30,7 @@ #if defined(__MVS__) #include <xti.h> #endif +#include <sys/un.h> #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP @@ -227,9 +228,22 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { assert(req != NULL); memset(&h, 0, sizeof h); - h.msg_name = &req->addr; - h.msg_namelen = (req->addr.ss_family == AF_INET6 ? - sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); + if (req->addr.ss_family == AF_UNSPEC) { + h.msg_name = NULL; + h.msg_namelen = 0; + } else { + h.msg_name = &req->addr; + if (req->addr.ss_family == AF_INET6) + h.msg_namelen = sizeof(struct sockaddr_in6); + else if (req->addr.ss_family == AF_INET) + h.msg_namelen = sizeof(struct sockaddr_in); + else if (req->addr.ss_family == AF_UNIX) + h.msg_namelen = sizeof(struct sockaddr_un); + else { + assert(0 && "unsupported address family"); + abort(); + } + } h.msg_iov = (struct iovec*) req->bufs; h.msg_iovlen = req->nbufs; @@ -263,16 +277,30 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { * are different from the BSDs: it _shares_ the port rather than steal it * from the current listener. While useful, it's not something we can emulate * on other platforms so we don't enable it. + * + * zOS does not support getsockname with SO_REUSEPORT option when using + * AF_UNIX. */ static int uv__set_reuse(int fd) { int yes; - -#if defined(SO_REUSEPORT) && !defined(__linux__) yes = 1; + +#if defined(SO_REUSEPORT) && defined(__MVS__) + struct sockaddr_in sockfd; + unsigned int sockfd_len = sizeof(sockfd); + if (getsockname(fd, (struct sockaddr*) &sockfd, &sockfd_len) == -1) + return UV__ERR(errno); + if (sockfd.sin_family == AF_UNIX) { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return UV__ERR(errno); + } else { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) + return UV__ERR(errno); + } +#elif defined(SO_REUSEPORT) && !defined(__linux__) if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) return UV__ERR(errno); #else - yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) return UV__ERR(errno); #endif @@ -383,6 +411,50 @@ static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, } +int uv__udp_connect(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen) { + int err; + + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + + do { + errno = 0; + err = connect(handle->io_watcher.fd, addr, addrlen); + } while (err == -1 && errno == EINTR); + + if (err) + return UV__ERR(errno); + + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; +} + + +int uv__udp_disconnect(uv_udp_t* handle) { + int r; + struct sockaddr addr; + + memset(&addr, 0, sizeof(addr)); + + addr.sa_family = AF_UNSPEC; + + do { + errno = 0; + r = connect(handle->io_watcher.fd, &addr, sizeof(addr)); + } while (r == -1 && errno == EINTR); + + if (r == -1 && errno != EAFNOSUPPORT) + return UV__ERR(errno); + + handle->flags &= ~UV_HANDLE_UDP_CONNECTED; + return 0; +} + + int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], @@ -395,9 +467,11 @@ int uv__udp_send(uv_udp_send_t* req, assert(nbufs > 0); - err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); - if (err) - return err; + if (addr) { + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + } /* It's legal for send_queue_count > 0 even when the write_queue is empty; * it means there are error-state requests in the write_completed_queue that @@ -407,7 +481,10 @@ int uv__udp_send(uv_udp_send_t* req, uv__req_init(handle->loop, req, UV_UDP_SEND); assert(addrlen <= sizeof(req->addr)); - memcpy(&req->addr, addr, addrlen); + if (addr == NULL) + req->addr.ss_family = AF_UNSPEC; + else + memcpy(&req->addr, addr, addrlen); req->send_cb = send_cb; req->handle = handle; req->nbufs = nbufs; @@ -459,9 +536,13 @@ int uv__udp_try_send(uv_udp_t* handle, if (handle->send_queue_count != 0) return UV_EAGAIN; - err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); - if (err) - return err; + if (addr) { + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + } else { + assert(handle->flags & UV_HANDLE_UDP_CONNECTED); + } memset(&h, 0, sizeof h); h.msg_name = (struct sockaddr*) addr; @@ -608,6 +689,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { uv__io_init(&handle->io_watcher, uv__udp_io, fd); QUEUE_INIT(&handle->write_queue); QUEUE_INIT(&handle->write_completed_queue); + return 0; } @@ -636,6 +718,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { return err; handle->io_watcher.fd = sock; + if (uv__udp_is_connected(handle)) + handle->flags |= UV_HANDLE_UDP_CONNECTED; + return 0; } @@ -743,13 +828,17 @@ int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); -#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || - defined(__MVS__) */ + +#else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || + defined(__MVS__)) */ return uv__setsockopt_maybe_char(handle, IP_TTL, IPV6_UNICAST_HOPS, ttl); + +#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || + defined(__MVS__) */ } @@ -851,23 +940,24 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) return 0; } - -int uv_udp_getsockname(const uv_udp_t* handle, +int uv_udp_getpeername(const uv_udp_t* handle, struct sockaddr* name, int* namelen) { - socklen_t socklen; - - if (handle->io_watcher.fd == -1) - return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen); +} - if (getsockname(handle->io_watcher.fd, name, &socklen)) - return UV__ERR(errno); +int uv_udp_getsockname(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { - *namelen = (int) socklen; - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen); } diff --git a/Utilities/cmlibuv/src/uv-common.c b/Utilities/cmlibuv/src/uv-common.c index 907ebf2..f4853d6 100644 --- a/Utilities/cmlibuv/src/uv-common.c +++ b/Utilities/cmlibuv/src/uv-common.c @@ -34,6 +34,7 @@ # include <malloc.h> /* malloc */ #else # include <net/if.h> /* if_nametoindex */ +# include <sys/un.h> /* AF_UNIX, sockaddr_un */ #endif @@ -223,6 +224,9 @@ int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) { memset(addr, 0, sizeof(*addr)); addr->sin6_family = AF_INET6; addr->sin6_port = htons(port); +#ifdef SIN6_LEN + addr->sin6_len = sizeof(*addr); +#endif zone_index = strchr(ip, '%'); if (zone_index != NULL) { @@ -315,17 +319,20 @@ int uv_tcp_connect(uv_connect_t* req, } -int uv_udp_send(uv_udp_send_t* req, - uv_udp_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - const struct sockaddr* addr, - uv_udp_send_cb send_cb) { +int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; + /* Disconnect the handle */ + if (addr == NULL) { + if (!(handle->flags & UV_HANDLE_UDP_CONNECTED)) + return UV_ENOTCONN; + + return uv__udp_disconnect(handle); + } + if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) @@ -333,6 +340,70 @@ int uv_udp_send(uv_udp_send_t* req, else return UV_EINVAL; + if (handle->flags & UV_HANDLE_UDP_CONNECTED) + return UV_EISCONN; + + return uv__udp_connect(handle, addr, addrlen); +} + + +int uv__udp_is_connected(uv_udp_t* handle) { + struct sockaddr_storage addr; + int addrlen; + if (handle->type != UV_UDP) + return 0; + + addrlen = sizeof(addr); + if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0) + return 0; + + return addrlen > 0; +} + + +int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) { + unsigned int addrlen; + + if (handle->type != UV_UDP) + return UV_EINVAL; + + if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED)) + return UV_EISCONN; + + if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED)) + return UV_EDESTADDRREQ; + + if (addr != NULL) { + if (addr->sa_family == AF_INET) + addrlen = sizeof(struct sockaddr_in); + else if (addr->sa_family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); +#if defined(AF_UNIX) && !defined(_WIN32) + else if (addr->sa_family == AF_UNIX) + addrlen = sizeof(struct sockaddr_un); +#endif + else + return UV_EINVAL; + } else { + addrlen = 0; + } + + return addrlen; +} + + +int uv_udp_send(uv_udp_send_t* req, + uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr, + uv_udp_send_cb send_cb) { + int addrlen; + + addrlen = uv__udp_check_before_send(handle, addr); + if (addrlen < 0) + return addrlen; + return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb); } @@ -341,17 +412,11 @@ int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr) { - unsigned int addrlen; - - if (handle->type != UV_UDP) - return UV_EINVAL; + int addrlen; - if (addr->sa_family == AF_INET) - addrlen = sizeof(struct sockaddr_in); - else if (addr->sa_family == AF_INET6) - addrlen = sizeof(struct sockaddr_in6); - else - return UV_EINVAL; + addrlen = uv__udp_check_before_send(handle, addr); + if (addrlen < 0) + return addrlen; return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen); } @@ -573,37 +638,66 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) { dent = dents[(*nbufs)++]; ent->name = dent->d_name; + ent->type = uv__fs_get_dirent_type(dent); + + return 0; +} + +uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent) { + uv_dirent_type_t type; + #ifdef HAVE_DIRENT_TYPES switch (dent->d_type) { case UV__DT_DIR: - ent->type = UV_DIRENT_DIR; + type = UV_DIRENT_DIR; break; case UV__DT_FILE: - ent->type = UV_DIRENT_FILE; + type = UV_DIRENT_FILE; break; case UV__DT_LINK: - ent->type = UV_DIRENT_LINK; + type = UV_DIRENT_LINK; break; case UV__DT_FIFO: - ent->type = UV_DIRENT_FIFO; + type = UV_DIRENT_FIFO; break; case UV__DT_SOCKET: - ent->type = UV_DIRENT_SOCKET; + type = UV_DIRENT_SOCKET; break; case UV__DT_CHAR: - ent->type = UV_DIRENT_CHAR; + type = UV_DIRENT_CHAR; break; case UV__DT_BLOCK: - ent->type = UV_DIRENT_BLOCK; + type = UV_DIRENT_BLOCK; break; default: - ent->type = UV_DIRENT_UNKNOWN; + type = UV_DIRENT_UNKNOWN; } #else - ent->type = UV_DIRENT_UNKNOWN; + type = UV_DIRENT_UNKNOWN; #endif - return 0; + return type; +} + +void uv__fs_readdir_cleanup(uv_fs_t* req) { + uv_dir_t* dir; + uv_dirent_t* dirents; + int i; + + if (req->ptr == NULL) + return; + + dir = req->ptr; + dirents = dir->dirents; + req->ptr = NULL; + + if (dirents == NULL) + return; + + for (i = 0; i < req->result; ++i) { + uv__free((char*) dirents[i].name); + dirents[i].name = NULL; + } } diff --git a/Utilities/cmlibuv/src/uv-common.h b/Utilities/cmlibuv/src/uv-common.h index 15ac4d0..f788161 100644 --- a/Utilities/cmlibuv/src/uv-common.h +++ b/Utilities/cmlibuv/src/uv-common.h @@ -103,6 +103,7 @@ enum { /* Only used by uv_udp_t handles. */ UV_HANDLE_UDP_PROCESSING = 0x01000000, + UV_HANDLE_UDP_CONNECTED = 0x02000000, /* Only used by uv_pipe_t handles. */ UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000, @@ -142,6 +143,14 @@ int uv__udp_bind(uv_udp_t* handle, unsigned int addrlen, unsigned int flags); +int uv__udp_connect(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen); + +int uv__udp_disconnect(uv_udp_t* handle); + +int uv__udp_is_connected(uv_udp_t* handle); + int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], @@ -184,6 +193,8 @@ size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs); int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value); void uv__fs_scandir_cleanup(uv_fs_t* req); +void uv__fs_readdir_cleanup(uv_fs_t* req); +uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent); int uv__next_timeout(const uv_loop_t* loop); void uv__run_timers(uv_loop_t* loop); diff --git a/Utilities/cmlibuv/src/uv-data-getter-setters.c b/Utilities/cmlibuv/src/uv-data-getter-setters.c index b7fcd4a..c302566 100644 --- a/Utilities/cmlibuv/src/uv-data-getter-setters.c +++ b/Utilities/cmlibuv/src/uv-data-getter-setters.c @@ -36,7 +36,7 @@ const char* uv_req_type_name(uv_req_type type) { case UV_REQ_TYPE_MAX: case UV_UNKNOWN_REQ: default: /* UV_REQ_TYPE_PRIVATE */ - return NULL; + break; } return NULL; } diff --git a/Utilities/cmlibuv/src/win/core.c b/Utilities/cmlibuv/src/win/core.c index 58309c6..e9d0a58 100644 --- a/Utilities/cmlibuv/src/win/core.c +++ b/Utilities/cmlibuv/src/win/core.c @@ -627,3 +627,26 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { int uv_cpumask_size(void) { return (int)(sizeof(DWORD_PTR) * 8); } + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen, + int delayed_error) { + + int result; + uv_os_fd_t fd; + + result = uv_fileno(handle, &fd); + if (result != 0) + return result; + + if (delayed_error) + return uv_translate_sys_error(delayed_error); + + result = func((SOCKET) fd, name, namelen); + if (result != 0) + return uv_translate_sys_error(WSAGetLastError()); + + return 0; +} diff --git a/Utilities/cmlibuv/src/win/fs.c b/Utilities/cmlibuv/src/win/fs.c index 65d936b..9e2f084 100644 --- a/Utilities/cmlibuv/src/win/fs.c +++ b/Utilities/cmlibuv/src/win/fs.c @@ -1125,6 +1125,137 @@ cleanup: uv__free(dirents); } +void fs__opendir(uv_fs_t* req) { + WCHAR* pathw; + size_t len; + const WCHAR* fmt; + WCHAR* find_path; + uv_dir_t* dir; + + pathw = req->file.pathw; + dir = NULL; + find_path = NULL; + + /* Figure out whether path is a file or a directory. */ + if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) { + SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY); + goto error; + } + + dir = uv__malloc(sizeof(*dir)); + if (dir == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + goto error; + } + + len = wcslen(pathw); + + if (len == 0) + fmt = L"./*"; + else if (IS_SLASH(pathw[len - 1])) + fmt = L"%s*"; + else + fmt = L"%s\\*"; + + find_path = uv__malloc(sizeof(WCHAR) * (len + 4)); + if (find_path == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + goto error; + } + + _snwprintf(find_path, len + 3, fmt, pathw); + dir->dir_handle = FindFirstFileW(find_path, &dir->find_data); + uv__free(find_path); + find_path = NULL; + if (dir->dir_handle == INVALID_HANDLE_VALUE && + GetLastError() != ERROR_FILE_NOT_FOUND) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + goto error; + } + + dir->need_find_call = FALSE; + req->ptr = dir; + SET_REQ_RESULT(req, 0); + return; + +error: + uv__free(dir); + uv__free(find_path); + req->ptr = NULL; +} + +void fs__readdir(uv_fs_t* req) { + uv_dir_t* dir; + uv_dirent_t* dirents; + uv__dirent_t dent; + unsigned int dirent_idx; + PWIN32_FIND_DATAW find_data; + unsigned int i; + int r; + + req->flags |= UV_FS_FREE_PTR; + dir = req->ptr; + dirents = dir->dirents; + memset(dirents, 0, dir->nentries * sizeof(*dir->dirents)); + find_data = &dir->find_data; + dirent_idx = 0; + + while (dirent_idx < dir->nentries) { + if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) { + if (GetLastError() == ERROR_NO_MORE_FILES) + break; + goto error; + } + + /* Skip "." and ".." entries. */ + if (find_data->cFileName[0] == L'.' && + (find_data->cFileName[1] == L'\0' || + (find_data->cFileName[1] == L'.' && + find_data->cFileName[2] == L'\0'))) { + dir->need_find_call = TRUE; + continue; + } + + r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName, + -1, + (char**) &dirents[dirent_idx].name); + if (r != 0) + goto error; + + /* Copy file type. */ + if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + dent.d_type = UV__DT_DIR; + else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) + dent.d_type = UV__DT_LINK; + else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) + dent.d_type = UV__DT_CHAR; + else + dent.d_type = UV__DT_FILE; + + dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent); + dir->need_find_call = TRUE; + ++dirent_idx; + } + + SET_REQ_RESULT(req, dirent_idx); + return; + +error: + SET_REQ_WIN32_ERROR(req, GetLastError()); + for (i = 0; i < dirent_idx; ++i) { + uv__free((char*) dirents[i].name); + dirents[i].name = NULL; + } +} + +void fs__closedir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = req->ptr; + FindClose(dir->dir_handle); + uv__free(req->ptr); + SET_REQ_RESULT(req, 0); +} INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, int do_lstat) { @@ -2039,6 +2170,9 @@ static void uv__fs_work(struct uv__work* w) { XX(MKDTEMP, mkdtemp) XX(RENAME, rename) XX(SCANDIR, scandir) + XX(READDIR, readdir) + XX(OPENDIR, opendir) + XX(CLOSEDIR, closedir) XX(LINK, link) XX(SYMLINK, symlink) XX(READLINK, readlink) @@ -2080,6 +2214,8 @@ void uv_fs_req_cleanup(uv_fs_t* req) { if (req->flags & UV_FS_FREE_PTR) { if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) uv__fs_scandir_cleanup(req); + else if (req->fs_type == UV_FS_READDIR) + uv__fs_readdir_cleanup(req); else uv__free(req->ptr); } @@ -2247,6 +2383,45 @@ int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, POST; } +int uv_fs_opendir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_OPENDIR); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) + return uv_translate_sys_error(err); + POST; +} + +int uv_fs_readdir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(UV_FS_READDIR); + + if (dir == NULL || + dir->dirents == NULL || + dir->dir_handle == INVALID_HANDLE_VALUE) { + return UV_EINVAL; + } + + req->ptr = dir; + POST; +} + +int uv_fs_closedir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(UV_FS_CLOSEDIR); + if (dir == NULL) + return UV_EINVAL; + req->ptr = dir; + POST; +} int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { diff --git a/Utilities/cmlibuv/src/win/handle.c b/Utilities/cmlibuv/src/win/handle.c index 9d76c3f..61e4df6 100644 --- a/Utilities/cmlibuv/src/win/handle.c +++ b/Utilities/cmlibuv/src/win/handle.c @@ -139,7 +139,6 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { case UV_FS_POLL: uv__fs_poll_close((uv_fs_poll_t*) handle); uv__handle_closing(handle); - uv_want_endgame(loop, handle); return; default: diff --git a/Utilities/cmlibuv/src/win/internal.h b/Utilities/cmlibuv/src/win/internal.h index 206ab5f..f7d8ccf 100644 --- a/Utilities/cmlibuv/src/win/internal.h +++ b/Utilities/cmlibuv/src/win/internal.h @@ -276,6 +276,14 @@ int uv__getpwuid_r(uv_passwd_t* pwd); int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8); int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16); +typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*); + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen, + int delayed_error); + /* * Process stdio handles. diff --git a/Utilities/cmlibuv/src/win/tcp.c b/Utilities/cmlibuv/src/win/tcp.c index 3ce5548..f2cb527 100644 --- a/Utilities/cmlibuv/src/win/tcp.c +++ b/Utilities/cmlibuv/src/win/tcp.c @@ -809,44 +809,24 @@ static int uv_tcp_try_connect(uv_connect_t* req, int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - int result; - - if (handle->socket == INVALID_SOCKET) { - return UV_EINVAL; - } - - if (handle->delayed_error) { - return uv_translate_sys_error(handle->delayed_error); - } - - result = getsockname(handle->socket, name, namelen); - if (result != 0) { - return uv_translate_sys_error(WSAGetLastError()); - } - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen, + handle->delayed_error); } int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - int result; - - if (handle->socket == INVALID_SOCKET) { - return UV_EINVAL; - } - - if (handle->delayed_error) { - return uv_translate_sys_error(handle->delayed_error); - } - - result = getpeername(handle->socket, name, namelen); - if (result != 0) { - return uv_translate_sys_error(WSAGetLastError()); - } - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen, + handle->delayed_error); } diff --git a/Utilities/cmlibuv/src/win/thread.c b/Utilities/cmlibuv/src/win/thread.c index fd4b7c9..89c53ad 100644 --- a/Utilities/cmlibuv/src/win/thread.c +++ b/Utilities/cmlibuv/src/win/thread.c @@ -112,9 +112,34 @@ static UINT __stdcall uv__thread_start(void* arg) { int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { + uv_thread_options_t params; + params.flags = UV_THREAD_NO_FLAGS; + return uv_thread_create_ex(tid, ¶ms, entry, arg); +} + +int uv_thread_create_ex(uv_thread_t* tid, + const uv_thread_options_t* params, + void (*entry)(void *arg), + void *arg) { struct thread_ctx* ctx; int err; HANDLE thread; + SYSTEM_INFO sysinfo; + size_t stack_size; + size_t pagesize; + + stack_size = + params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0; + + if (stack_size != 0) { + GetNativeSystemInfo(&sysinfo); + pagesize = (size_t)sysinfo.dwPageSize; + /* Round up to the nearest page boundary. */ + stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); + + if ((unsigned)stack_size != stack_size) + return UV_EINVAL; + } ctx = uv__malloc(sizeof(*ctx)); if (ctx == NULL) @@ -126,7 +151,7 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { /* Create the thread in suspended state so we have a chance to pass * its own creation handle to it */ thread = (HANDLE) _beginthreadex(NULL, - 0, + (unsigned)stack_size, uv__thread_start, ctx, CREATE_SUSPENDED, diff --git a/Utilities/cmlibuv/src/win/tty.c b/Utilities/cmlibuv/src/win/tty.c index f38e9a8..a98fe26 100644 --- a/Utilities/cmlibuv/src/win/tty.c +++ b/Utilities/cmlibuv/src/win/tty.c @@ -736,8 +736,8 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, /* Ignore keyup events, unless the left alt key was held and a valid * unicode character was emitted. */ if (!KEV.bKeyDown && - KEV.wVirtualKeyCode != VK_MENU && - KEV.uChar.UnicodeChar != 0) { + (KEV.wVirtualKeyCode != VK_MENU || + KEV.uChar.UnicodeChar == 0)) { continue; } diff --git a/Utilities/cmlibuv/src/win/udp.c b/Utilities/cmlibuv/src/win/udp.c index 37df849..8aeeab3 100644 --- a/Utilities/cmlibuv/src/win/udp.c +++ b/Utilities/cmlibuv/src/win/udp.c @@ -36,22 +36,27 @@ const unsigned int uv_active_udp_streams_threshold = 0; /* A zero-size buffer for use by uv_udp_read */ static char uv_zero_[] = ""; - -int uv_udp_getsockname(const uv_udp_t* handle, +int uv_udp_getpeername(const uv_udp_t* handle, struct sockaddr* name, int* namelen) { - int result; - if (handle->socket == INVALID_SOCKET) { - return UV_EINVAL; - } + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen, + 0); +} - result = getsockname(handle->socket, name, namelen); - if (result != 0) { - return uv_translate_sys_error(WSAGetLastError()); - } - return 0; +int uv_udp_getsockname(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { + + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen, + 0); } @@ -784,6 +789,18 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int value) { } +int uv__udp_is_bound(uv_udp_t* handle) { + struct sockaddr_storage addr; + int addrlen; + + addrlen = sizeof(addr); + if (uv_udp_getsockname(handle, (struct sockaddr*) &addr, &addrlen) != 0) + return 0; + + return addrlen > 0; +} + + int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { WSAPROTOCOL_INFOW protocol_info; int opt_len; @@ -803,7 +820,16 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { handle, sock, protocol_info.iAddressFamily); - return uv_translate_sys_error(err); + if (err) + return uv_translate_sys_error(err); + + if (uv__udp_is_bound(handle)) + handle->flags |= UV_HANDLE_BOUND; + + if (uv__udp_is_connected(handle)) + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; } @@ -880,6 +906,50 @@ int uv__udp_bind(uv_udp_t* handle, } +int uv__udp_connect(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen) { + const struct sockaddr* bind_addr; + int err; + + 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 = connect(handle->socket, addr, addrlen); + if (err) + return uv_translate_sys_error(err); + + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; +} + + +int uv__udp_disconnect(uv_udp_t* handle) { + int err; + struct sockaddr addr; + + memset(&addr, 0, sizeof(addr)); + + err = connect(handle->socket, &addr, sizeof(addr)); + if (err) + return uv_translate_sys_error(err); + + handle->flags &= ~UV_HANDLE_UDP_CONNECTED; + return 0; +} + + /* This function is an egress point, i.e. it returns libuv errors rather than * system errors. */ @@ -900,6 +970,7 @@ int uv__udp_send(uv_udp_send_t* req, 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); @@ -925,9 +996,11 @@ int uv__udp_try_send(uv_udp_t* handle, assert(nbufs > 0); - err = uv__convert_to_localhost_if_unspecified(addr, &converted); - if (err) - return err; + if (addr != NULL) { + err = uv__convert_to_localhost_if_unspecified(addr, &converted); + if (err) + return err; + } /* Already sending a message.*/ if (handle->send_queue_count != 0) diff --git a/Utilities/cmlibuv/src/win/util.c b/Utilities/cmlibuv/src/win/util.c index 9237891..7ca8321 100644 --- a/Utilities/cmlibuv/src/win/util.c +++ b/Utilities/cmlibuv/src/win/util.c @@ -59,13 +59,6 @@ # define UNLEN 256 #endif -/* - Max hostname length. The Windows gethostname() documentation states that 256 - bytes will always be large enough to hold the null-terminated hostname. -*/ -#ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 256 -#endif /* Maximum environment variable size, including the terminating null */ #define MAX_ENV_VAR_LENGTH 32767 @@ -327,6 +320,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + uv_pid_t uv_os_getpid(void) { return GetCurrentProcessId(); } @@ -684,12 +682,9 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { NULL, (BYTE*)&cpu_brand, &cpu_brand_size); - if (err != ERROR_SUCCESS) { - RegCloseKey(processor_key); - goto error; - } - RegCloseKey(processor_key); + if (err != ERROR_SUCCESS) + goto error; cpu_info = &cpu_infos[i]; cpu_info->speed = cpu_speed; @@ -713,9 +708,11 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { return 0; error: - /* This is safe because the cpu_infos array is zeroed on allocation. */ - for (i = 0; i < cpu_count; i++) - uv__free(cpu_infos[i].model); + if (cpu_infos != NULL) { + /* This is safe because the cpu_infos array is zeroed on allocation. */ + for (i = 0; i < cpu_count; i++) + uv__free(cpu_infos[i].model); + } uv__free(cpu_infos); uv__free(sppi); @@ -1510,7 +1507,7 @@ int uv_os_unsetenv(const char* name) { int uv_os_gethostname(char* buffer, size_t* size) { - char buf[MAXHOSTNAMELEN + 1]; + char buf[UV_MAXHOSTNAMESIZE]; size_t len; if (buffer == NULL || size == NULL || *size == 0) @@ -1634,6 +1631,10 @@ int uv_os_uname(uv_utsname_t* buffer) { https://github.com/gagern/gnulib/blob/master/lib/uname.c */ OSVERSIONINFOW os_info; SYSTEM_INFO system_info; + HKEY registry_key; + WCHAR product_name_w[256]; + DWORD product_name_w_size; + int version_size; int processor_level; int r; @@ -1658,16 +1659,56 @@ int uv_os_uname(uv_utsname_t* buffer) { } /* Populate the version field. */ - if (WideCharToMultiByte(CP_UTF8, - 0, - os_info.szCSDVersion, - -1, - buffer->version, - sizeof(buffer->version), - NULL, - NULL) == 0) { - r = uv_translate_sys_error(GetLastError()); - goto error; + version_size = 0; + r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + 0, + KEY_QUERY_VALUE, + ®istry_key); + + if (r == ERROR_SUCCESS) { + product_name_w_size = sizeof(product_name_w); + r = RegGetValueW(registry_key, + NULL, + L"ProductName", + RRF_RT_REG_SZ, + NULL, + (PVOID) product_name_w, + &product_name_w_size); + RegCloseKey(registry_key); + + if (r == ERROR_SUCCESS) { + version_size = WideCharToMultiByte(CP_UTF8, + 0, + product_name_w, + -1, + buffer->version, + sizeof(buffer->version), + NULL, + NULL); + if (version_size == 0) { + r = uv_translate_sys_error(GetLastError()); + goto error; + } + } + } + + /* Append service pack information to the version if present. */ + if (os_info.szCSDVersion[0] != L'\0') { + if (version_size > 0) + buffer->version[version_size - 1] = ' '; + + if (WideCharToMultiByte(CP_UTF8, + 0, + os_info.szCSDVersion, + -1, + buffer->version + version_size, + sizeof(buffer->version) - version_size, + NULL, + NULL) == 0) { + r = uv_translate_sys_error(GetLastError()); + goto error; + } } /* Populate the sysname field. */ @@ -1744,3 +1785,20 @@ error: buffer->machine[0] = '\0'; return r; } + +int uv_gettimeofday(uv_timeval64_t* tv) { + /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */ + const uint64_t epoch = (uint64_t) 116444736000000000ULL; + FILETIME file_time; + ULARGE_INTEGER ularge; + + if (tv == NULL) + return UV_EINVAL; + + GetSystemTimeAsFileTime(&file_time); + ularge.LowPart = file_time.dwLowDateTime; + ularge.HighPart = file_time.dwHighDateTime; + tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L); + tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10); + return 0; +} diff --git a/Utilities/cmlibuv/src/win/winsock.c b/Utilities/cmlibuv/src/win/winsock.c index 5e7da2a..5820ba9 100644 --- a/Utilities/cmlibuv/src/win/winsock.c +++ b/Utilities/cmlibuv/src/win/winsock.c @@ -87,12 +87,6 @@ void uv_winsock_init(void) { WSAPROTOCOL_INFOW protocol_info; int opt_len; - /* Initialize winsock */ - errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (errorno != 0) { - uv_fatal_error(errorno, "WSAStartup"); - } - /* Set implicit binding address used by connectEx */ if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) { abort(); @@ -102,6 +96,15 @@ void uv_winsock_init(void) { abort(); } + /* Skip initialization in safe mode without network support */ + if (1 == GetSystemMetrics(SM_CLEANBOOT)) return; + + /* Initialize winsock */ + errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (errorno != 0) { + uv_fatal_error(errorno, "WSAStartup"); + } + /* Detect non-IFS LSPs */ dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); @@ -261,6 +261,17 @@ CMAKE_CXX_SOURCES="\ cmAddSubDirectoryCommand \ cmAddTestCommand \ cmArgumentParser \ + cmBinUtilsLinker \ + cmBinUtilsLinuxELFGetRuntimeDependenciesTool \ + cmBinUtilsLinuxELFLinker \ + cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool \ + cmBinUtilsMacOSMachOGetRuntimeDependenciesTool \ + cmBinUtilsMacOSMachOLinker \ + cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool \ + cmBinUtilsWindowsPEGetRuntimeDependenciesTool \ + cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool \ + cmBinUtilsWindowsPELinker \ + cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool \ cmBreakCommand \ cmBuildCommand \ cmCMakeMinimumRequired \ @@ -357,6 +368,8 @@ CMAKE_CXX_SOURCES="\ cmInstallTargetGenerator \ cmInstallTargetsCommand \ cmInstalledFile \ + cmLDConfigLDConfigTool \ + cmLDConfigTool \ cmLinkDirectoriesCommand \ cmLinkItem \ cmLinkLineComputer \ @@ -388,12 +401,12 @@ CMAKE_CXX_SOURCES="\ cmPolicies \ cmProcessOutput \ cmProjectCommand \ - cmProperty \ cmPropertyDefinition \ cmPropertyDefinitionMap \ cmPropertyMap \ cmReturnCommand \ cmRulePlaceholderExpander \ + cmRuntimeDependencyArchive \ cmScriptGenerator \ cmSearchPath \ cmSeparateArgumentsCommand \ @@ -443,7 +456,9 @@ CMAKE_CXX_SOURCES="\ if ${cmake_system_mingw}; then CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES}\ cmGlobalMSYSMakefileGenerator \ - cmGlobalMinGWMakefileGenerator" + cmGlobalMinGWMakefileGenerator \ + cmVSSetupHelper \ + " fi LexerParser_CXX_SOURCES="\ @@ -1370,7 +1385,7 @@ libs="" uv_c_flags="" if ${cmake_system_mingw}; then uv_c_flags="${uv_c_flags} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0600" - libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv" + libs="${libs} -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -lole32 -loleaut32" else uv_c_flags="${uv_c_flags} -DCMAKE_BOOTSTRAP" case "${cmake_system}" in |