diff options
159 files changed, 3931 insertions, 1720 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f16e79..e88e925 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -513,6 +513,7 @@ macro (CMAKE_BUILD_UTILITIES) set(ENABLE_NETTLE OFF CACHE INTERNAL "Enable use of Nettle") set(ENABLE_OPENSSL ${CMAKE_USE_OPENSSL} CACHE INTERNAL "Enable use of OpenSSL") set(ENABLE_LZMA ON CACHE INTERNAL "Enable the use of the system LZMA library if found") + set(ENABLE_LZ4 OFF CACHE INTERNAL "Enable the use of the system LZ4 library if found") set(ENABLE_LZO OFF CACHE INTERNAL "Enable the use of the system LZO library if found") set(ENABLE_ZLIB ON CACHE INTERNAL "Enable the use of the system ZLIB library if found") set(ENABLE_BZip2 ON CACHE INTERNAL "Enable the use of the system BZip2 library if found") diff --git a/Help/command/link_directories.rst b/Help/command/link_directories.rst index 5c64bc6..1dce9a0 100644 --- a/Help/command/link_directories.rst +++ b/Help/command/link_directories.rst @@ -1,19 +1,51 @@ link_directories ---------------- -Specify directories in which the linker will look for libraries. +Add directories in which the linker will look for libraries. :: - link_directories(directory1 directory2 ...) + link_directories([AFTER|BEFORE] directory1 [directory2 ...]) -Specify the paths in which the linker should search for libraries. -The command will apply only to targets created after it is called. +Add the paths in which the linker should search for libraries. Relative paths given to this command are interpreted as relative to the current source directory, see :policy:`CMP0015`. -Note that this command is rarely necessary. Library locations -returned by :command:`find_package` and :command:`find_library` are -absolute paths. Pass these absolute library file paths directly to the -:command:`target_link_libraries` command. CMake will ensure the linker finds -them. +The directories are added to the :prop_dir:`LINK_DIRECTORIES` directory +property for the current ``CMakeLists.txt`` file, converting relative +paths to absolute as needed. +The command will apply only to targets created after it is called. + +By default the directories specified are appended onto the current list of +directories. This default behavior can be changed by setting +:variable:`CMAKE_LINK_DIRECTORIES_BEFORE` to ``ON``. By using +``AFTER`` or ``BEFORE`` explicitly, you can select between appending and +prepending, independent of the default. + +Arguments to ``link_directories`` may use "generator expressions" with +the syntax "$<...>". See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. See the :manual:`cmake-buildsystem(7)` +manual for more on defining buildsystem properties. + +.. note:: + + This command is rarely necessary and should be avoided where there are + other choices. Prefer to pass full absolute paths to libraries where + possible, since this ensures the correct library will always be linked. + The :command:`find_library` command provides the full path, which can + generally be used directly in calls to :command:`target_link_libraries`. + Situations where a library search path may be needed include: + + - Project generators like Xcode where the user can switch target + architecture at build time, but a full path to a library cannot + be used because it only provides one architecture (i.e. it is not + a universal binary). + - Libraries may themselves have other private library dependencies + that expect to be found via ``RPATH`` mechanisms, but some linkers + are not able to fully decode those paths (e.g. due to the presence + of things like ``$ORIGIN``). + + If a library search path must be provided, prefer to localize the effect + where possible by using the :command:`target_link_directories` command + rather than ``link_directories()``. The target-specific command can also + control how the search directories propagate to other dependent targets. diff --git a/Help/command/target_link_directories.rst b/Help/command/target_link_directories.rst new file mode 100644 index 0000000..b46aac0 --- /dev/null +++ b/Help/command/target_link_directories.rst @@ -0,0 +1,55 @@ +target_link_directories +----------------------- + +Add link directories to a target. + +:: + + target_link_directories(<target> [BEFORE] + <INTERFACE|PUBLIC|PRIVATE> [items1...] + [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...]) + +Specify the paths in which the linker should search for libraries when +linking a given target. Each item can be an absolute or relative path, +with the latter being interpreted as relative to the current source +directory. These items will be added to the link command. + +The named ``<target>`` must have been created by a command such as +:command:`add_executable` or :command:`add_library` and must not be an +:ref:`ALIAS target <Alias Targets>`. + +The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to +specify the scope of the items that follow them. ``PRIVATE`` and +``PUBLIC`` items will populate the :prop_tgt:`LINK_DIRECTORIES` property +of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the +:prop_tgt:`INTERFACE_LINK_DIRECTORIES` property of ``<target>`` +(:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items). +Each item specifies a link directory and will be converted to an absolute +path if necessary before adding it to the relevant property. Repeated +calls for the same ``<target>`` append items in the order called. + +If ``BEFORE`` is specified, the content will be prepended to the relevant +property instead of being appended. + +Arguments to ``target_link_directories`` may use "generator expressions" +with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. See the :manual:`cmake-buildsystem(7)` +manual for more on defining buildsystem properties. + +.. note:: + + This command is rarely necessary and should be avoided where there are + other choices. Prefer to pass full absolute paths to libraries where + possible, since this ensures the correct library will always be linked. + The :command:`find_library` command provides the full path, which can + generally be used directly in calls to :command:`target_link_libraries`. + Situations where a library search path may be needed include: + + - Project generators like Xcode where the user can switch target + architecture at build time, but a full path to a library cannot + be used because it only provides one architecture (i.e. it is not + a universal binary). + - Libraries may themselves have other private library dependencies + that expect to be found via ``RPATH`` mechanisms, but some linkers + are not able to fully decode those paths (e.g. due to the presence + of things like ``$ORIGIN``). diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst index 37d750d..26021cc 100644 --- a/Help/cpack_gen/deb.rst +++ b/Help/cpack_gen/deb.rst @@ -518,6 +518,26 @@ List of CPack Deb generator specific variables: This value is not interpreted. It is possible to pass an optional revision number of the referenced source package as well. +Packaging of debug information +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Dbgsym packages contain debug symbols for debugging packaged binaries. + +Dbgsym packaging has its own set of variables: + +.. variable:: CPACK_DEBIAN_DEBUGINFO_PACKAGE + CPACK_DEBIAN_<component>_DEBUGINFO_PACKAGE + + Enable generation of dbgsym .ddeb package(s). + + * Mandatory : NO + * Default : OFF + +.. note:: + + Binaries must contain debug symbols before packaging so use either ``Debug`` + or ``RelWithDebInfo`` for :variable:`CMAKE_BUILD_TYPE` variable value. + Building Debian packages on Windows ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/cpack_gen/rpm.rst b/Help/cpack_gen/rpm.rst index 0214766..5c543ff 100644 --- a/Help/cpack_gen/rpm.rst +++ b/Help/cpack_gen/rpm.rst @@ -85,7 +85,7 @@ List of CPack RPM generator specific variables: By using user provided spec file, rpm macro extensions such as for generating debuginfo packages or by simply using multiple components more than one rpm file may be generated, either from a single spec file or from - multiple spec files (each component execution produces it's own spec file). + multiple spec files (each component execution produces its own spec file). In such cases duplicate file names may occur as a result of this variable setting or spec file content structure. Duplicate files get overwritten and it is up to the packager to set the variables in a manner that will @@ -749,7 +749,7 @@ Packaging of debug information Debuginfo packages contain debug symbols and sources for debugging packaged binaries. -Debuginfo RPM packaging has it's own set of variables: +Debuginfo RPM packaging has its own set of variables: .. variable:: CPACK_RPM_DEBUGINFO_PACKAGE CPACK_RPM_<component>_DEBUGINFO_PACKAGE @@ -911,7 +911,7 @@ directories. different binary rpm packages on different platforms depending on the platform's packaging rules. -Source RPM packaging has it's own set of variables: +Source RPM packaging has its own set of variables: .. variable:: CPACK_RPM_PACKAGE_SOURCES diff --git a/Help/generator/Visual Studio 10 2010.rst b/Help/generator/Visual Studio 10 2010.rst index b8d6872..0446b8c 100644 --- a/Help/generator/Visual Studio 10 2010.rst +++ b/Help/generator/Visual Studio 10 2010.rst @@ -3,18 +3,6 @@ Visual Studio 10 2010 Generates Visual Studio 10 (VS 2010) project files. -The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set -to specify a target platform name (architecture). - -For compatibility with CMake versions prior to 3.1, one may specify -a target platform name optionally at the end of this generator name: - -``Visual Studio 10 2010 Win64`` - Specify target platform ``x64``. - -``Visual Studio 10 2010 IA64`` - Specify target platform ``Itanium``. - For compatibility with CMake versions prior to 3.0, one may specify this generator using the name ``Visual Studio 10`` without the year component. @@ -24,6 +12,27 @@ Project Types Only Visual C++ and C# projects may be generated. Other types of projects (Database, Website, etc.) are not supported. +Platform Selection +^^^^^^^^^^^^^^^^^^ + +The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps +via the :manual:`cmake(1)` ``-A`` option, to specify a target platform +name (architecture). For example: + +* ``cmake -G "Visual Studio 10 2010" -A Win32`` +* ``cmake -G "Visual Studio 10 2010" -A x64`` +* ``cmake -G "Visual Studio 10 2010" -A Itanium`` + +For compatibility with CMake versions prior to 3.1, one may specify +a target platform name optionally at the end of the generator name. +This is supported only for: + +``Visual Studio 10 2010 Win64`` + Specify target platform ``x64``. + +``Visual Studio 10 2010 IA64`` + Specify target platform ``Itanium``. + Toolset Selection ^^^^^^^^^^^^^^^^^ diff --git a/Help/generator/Visual Studio 11 2012.rst b/Help/generator/Visual Studio 11 2012.rst index 8e9998a..8fddbb3 100644 --- a/Help/generator/Visual Studio 11 2012.rst +++ b/Help/generator/Visual Studio 11 2012.rst @@ -3,11 +3,31 @@ Visual Studio 11 2012 Generates Visual Studio 11 (VS 2012) project files. -The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set -to specify a target platform name (architecture). +For compatibility with CMake versions prior to 3.0, one may specify this +generator using the name "Visual Studio 11" without the year component. + +Project Types +^^^^^^^^^^^^^ + +Only Visual C++ and C# projects may be generated. Other types of +projects (JavaScript, Database, Website, etc.) are not supported. + +Platform Selection +^^^^^^^^^^^^^^^^^^ + +The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps +via the :manual:`cmake(1)` ``-A`` option, to specify a target platform +name (architecture). For example: + +* ``cmake -G "Visual Studio 11 2012" -A Win32`` +* ``cmake -G "Visual Studio 11 2012" -A x64`` +* ``cmake -G "Visual Studio 11 2012" -A ARM`` +* ``cmake -G "Visual Studio 11 2012" -A <WinCE-SDK>`` + (Specify a target platform matching a Windows CE SDK name.) For compatibility with CMake versions prior to 3.1, one may specify -a target platform name optionally at the end of this generator name: +a target platform name optionally at the end of the generator name. +This is supported only for: ``Visual Studio 11 2012 Win64`` Specify target platform ``x64``. @@ -18,15 +38,6 @@ a target platform name optionally at the end of this generator name: ``Visual Studio 11 2012 <WinCE-SDK>`` Specify target platform matching a Windows CE SDK name. -For compatibility with CMake versions prior to 3.0, one may specify this -generator using the name "Visual Studio 11" without the year component. - -Project Types -^^^^^^^^^^^^^ - -Only Visual C++ and C# projects may be generated. Other types of -projects (JavaScript, Database, Website, etc.) are not supported. - Toolset Selection ^^^^^^^^^^^^^^^^^ diff --git a/Help/generator/Visual Studio 12 2013.rst b/Help/generator/Visual Studio 12 2013.rst index 03f7586..8b4c162 100644 --- a/Help/generator/Visual Studio 12 2013.rst +++ b/Help/generator/Visual Studio 12 2013.rst @@ -3,18 +3,6 @@ Visual Studio 12 2013 Generates Visual Studio 12 (VS 2013) project files. -The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set -to specify a target platform name (architecture). - -For compatibility with CMake versions prior to 3.1, one may specify -a target platform name optionally at the end of this generator name: - -``Visual Studio 12 2013 Win64`` - Specify target platform ``x64``. - -``Visual Studio 12 2013 ARM`` - Specify target platform ``ARM``. - For compatibility with CMake versions prior to 3.0, one may specify this generator using the name "Visual Studio 12" without the year component. @@ -24,6 +12,27 @@ Project Types Only Visual C++ and C# projects may be generated. Other types of projects (JavaScript, Powershell, Python, etc.) are not supported. +Platform Selection +^^^^^^^^^^^^^^^^^^ + +The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps +via the :manual:`cmake(1)` ``-A`` option, to specify a target platform +name (architecture). For example: + +* ``cmake -G "Visual Studio 12 2013" -A Win32`` +* ``cmake -G "Visual Studio 12 2013" -A x64`` +* ``cmake -G "Visual Studio 12 2013" -A ARM`` + +For compatibility with CMake versions prior to 3.1, one may specify +a target platform name optionally at the end of the generator name. +This is supported only for: + +``Visual Studio 12 2013 Win64`` + Specify target platform ``x64``. + +``Visual Studio 12 2013 ARM`` + Specify target platform ``ARM``. + Toolset Selection ^^^^^^^^^^^^^^^^^ diff --git a/Help/generator/Visual Studio 14 2015.rst b/Help/generator/Visual Studio 14 2015.rst index e55bc73..917d8e5 100644 --- a/Help/generator/Visual Studio 14 2015.rst +++ b/Help/generator/Visual Studio 14 2015.rst @@ -3,11 +3,26 @@ Visual Studio 14 2015 Generates Visual Studio 14 (VS 2015) project files. -The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set -to specify a target platform name (architecture). +Project Types +^^^^^^^^^^^^^ + +Only Visual C++ and C# projects may be generated. Other types of +projects (JavaScript, Powershell, Python, etc.) are not supported. + +Platform Selection +^^^^^^^^^^^^^^^^^^ + +The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps +via the :manual:`cmake(1)` ``-A`` option, to specify a target platform +name (architecture). For example: + +* ``cmake -G "Visual Studio 14 2015" -A Win32`` +* ``cmake -G "Visual Studio 14 2015" -A x64`` +* ``cmake -G "Visual Studio 14 2015" -A ARM`` For compatibility with CMake versions prior to 3.1, one may specify -a target platform name optionally at the end of this generator name: +a target platform name optionally at the end of the generator name. +This is supported only for: ``Visual Studio 14 2015 Win64`` Specify target platform ``x64``. @@ -15,12 +30,6 @@ a target platform name optionally at the end of this generator name: ``Visual Studio 14 2015 ARM`` Specify target platform ``ARM``. -Project Types -^^^^^^^^^^^^^ - -Only Visual C++ and C# projects may be generated. Other types of -projects (JavaScript, Powershell, Python, etc.) are not supported. - Toolset Selection ^^^^^^^^^^^^^^^^^ diff --git a/Help/generator/Visual Studio 15 2017.rst b/Help/generator/Visual Studio 15 2017.rst index 809be4b..42a3bb6 100644 --- a/Help/generator/Visual Studio 15 2017.rst +++ b/Help/generator/Visual Studio 15 2017.rst @@ -3,18 +3,6 @@ Visual Studio 15 2017 Generates Visual Studio 15 (VS 2017) project files. -The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set -to specify a target platform name (architecture). - -For compatibility with CMake versions prior to 3.1, one may specify -a target platform name optionally at the end of this generator name: - -``Visual Studio 15 2017 Win64`` - Specify target platform ``x64``. - -``Visual Studio 15 2017 ARM`` - Specify target platform ``ARM``. - Project Types ^^^^^^^^^^^^^ @@ -37,6 +25,28 @@ one of the instances, that instance will be used. Otherwise, if more than one instance is installed we do not define which one is chosen by default. +Platform Selection +^^^^^^^^^^^^^^^^^^ + +The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps +via the :manual:`cmake(1)` ``-A`` option, to specify a target platform +name (architecture). For example: + +* ``cmake -G "Visual Studio 15 2017" -A Win32`` +* ``cmake -G "Visual Studio 15 2017" -A x64`` +* ``cmake -G "Visual Studio 15 2017" -A ARM`` +* ``cmake -G "Visual Studio 15 2017" -A ARM64`` + +For compatibility with CMake versions prior to 3.1, one may specify +a target platform name optionally at the end of the generator name. +This is supported only for: + +``Visual Studio 15 2017 Win64`` + Specify target platform ``x64``. + +``Visual Studio 15 2017 ARM`` + Specify target platform ``ARM``. + Toolset Selection ^^^^^^^^^^^^^^^^^ diff --git a/Help/generator/Visual Studio 9 2008.rst b/Help/generator/Visual Studio 9 2008.rst index 40471b9..a29033f 100644 --- a/Help/generator/Visual Studio 9 2008.rst +++ b/Help/generator/Visual Studio 9 2008.rst @@ -3,11 +3,22 @@ Visual Studio 9 2008 Generates Visual Studio 9 2008 project files. -The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set -to specify a target platform name. +Platform Selection +^^^^^^^^^^^^^^^^^^ + +The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps +via the :manual:`cmake(1)` ``-A`` option, to specify a target platform +name (architecture). For example: + +* ``cmake -G "Visual Studio 9 2008" -A Win32`` +* ``cmake -G "Visual Studio 9 2008" -A x64`` +* ``cmake -G "Visual Studio 9 2008" -A Itanium`` +* ``cmake -G "Visual Studio 9 2008" -A <WinCE-SDK>`` + (Specify a target platform matching a Windows CE SDK name.) For compatibility with CMake versions prior to 3.1, one may specify -a target platform name optionally at the end of this generator name: +a target platform name optionally at the end of the generator name. +This is supported only for: ``Visual Studio 9 2008 Win64`` Specify target platform ``x64``. diff --git a/Help/manual/cmake-commands.7.rst b/Help/manual/cmake-commands.7.rst index 753647d..0cc5fca 100644 --- a/Help/manual/cmake-commands.7.rst +++ b/Help/manual/cmake-commands.7.rst @@ -111,6 +111,7 @@ These commands are available only in CMake projects. /command/target_compile_features /command/target_compile_options /command/target_include_directories + /command/target_link_directories /command/target_link_libraries /command/target_link_options /command/target_sources diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 904ebee..2cc52fe 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -57,6 +57,7 @@ Policies Introduced by CMake 3.13 .. toctree:: :maxdepth: 1 + CMP0081: Relative paths not allowed in LINK_DIRECTORIES target property. </policy/CMP0081> CMP0080: BundleUtilities cannot be included at configure time. </policy/CMP0080> CMP0079: target_link_libraries allows use with targets in other directories. </policy/CMP0079> CMP0078: UseSWIG generates standard target names. </policy/CMP0078> diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 8ccd7f6..5c3eb81 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -228,6 +228,7 @@ Properties on Targets /prop_tgt/INTERFACE_COMPILE_OPTIONS /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES /prop_tgt/INTERFACE_LINK_DEPENDS + /prop_tgt/INTERFACE_LINK_DIRECTORIES /prop_tgt/INTERFACE_LINK_LIBRARIES /prop_tgt/INTERFACE_LINK_OPTIONS /prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE @@ -252,6 +253,7 @@ Properties on Targets /prop_tgt/LINK_DEPENDS_NO_SHARED /prop_tgt/LINK_DEPENDS /prop_tgt/LINKER_LANGUAGE + /prop_tgt/LINK_DIRECTORIES /prop_tgt/LINK_FLAGS_CONFIG /prop_tgt/LINK_FLAGS /prop_tgt/LINK_INTERFACE_LIBRARIES_CONFIG diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index f217f11..9dd36ed 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -179,6 +179,7 @@ Variables that Change Behavior /variable/CMAKE_INSTALL_PREFIX /variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT /variable/CMAKE_LIBRARY_PATH + /variable/CMAKE_LINK_DIRECTORIES_BEFORE /variable/CMAKE_MFC_FLAG /variable/CMAKE_MODULE_PATH /variable/CMAKE_NOT_USING_CONFIG_FLAGS diff --git a/Help/policy/CMP0081.rst b/Help/policy/CMP0081.rst new file mode 100644 index 0000000..d3b2872 --- /dev/null +++ b/Help/policy/CMP0081.rst @@ -0,0 +1,22 @@ +CMP0081 +------- + +Relative paths not allowed in :prop_tgt:`LINK_DIRECTORIES` target property. + +CMake 3.12 and lower allowed the :prop_dir:`LINK_DIRECTORIES` directory +property to contain relative paths. The base path for such relative +entries is not well defined. CMake 3.13 and later will issue a +``FATAL_ERROR`` if the :prop_tgt:`LINK_DIRECTORIES` target property +(which is initialized by the :prop_dir:`LINK_DIRECTORIES` directory property) +contains a relative path. + +The ``OLD`` behavior for this policy is not to warn about relative paths +in the :prop_tgt:`LINK_DIRECTORIES` target property. The ``NEW`` behavior for +this policy is to issue a ``FATAL_ERROR`` if :prop_tgt:`LINK_DIRECTORIES` +contains a relative path. + +This policy was introduced in CMake version 3.13. CMake version +|release| warns when the policy is not set 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_dir/LINK_DIRECTORIES.rst b/Help/prop_dir/LINK_DIRECTORIES.rst index fa37576..f9fb815 100644 --- a/Help/prop_dir/LINK_DIRECTORIES.rst +++ b/Help/prop_dir/LINK_DIRECTORIES.rst @@ -3,6 +3,15 @@ LINK_DIRECTORIES List of linker search directories. -This read-only property specifies the list of directories given so far -to the link_directories command. It is intended for debugging -purposes. +This property holds a :ref:`;-list <CMake Language Lists>` of directories +and is typically populated using the :command:`link_directories` command. +It gets its initial value from its parent directory, if it has one. + +The directory property is used to initialize the :prop_tgt:`LINK_DIRECTORIES` +target property when a target is created. That target property is used +by the generators to set the library search directories for the linker. + +Contents of ``LINK_DIRECTORIES`` may use "generator expressions" with +the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` +manual for available expressions. See the :manual:`cmake-buildsystem(7)` +manual for more on defining buildsystem properties. diff --git a/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst b/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst new file mode 100644 index 0000000..56a4ec0 --- /dev/null +++ b/Help/prop_tgt/INTERFACE_LINK_DIRECTORIES.rst @@ -0,0 +1,9 @@ +INTERFACE_LINK_DIRECTORIES +-------------------------- + +.. |property_name| replace:: link directories +.. |command_name| replace:: :command:`target_link_directories` +.. |PROPERTY_INTERFACE_NAME| replace:: ``INTERFACE_LINK_DIRECTORIES`` +.. |PROPERTY_LINK| replace:: :prop_tgt:`LINK_DIRECTORIES` +.. |PROPERTY_GENEX| replace:: ``$<TARGET_PROPERTY:foo,INTERFACE_LINK_DIRECTORIES>`` +.. include:: INTERFACE_BUILD_PROPERTY.txt diff --git a/Help/prop_tgt/LINK_DIRECTORIES.rst b/Help/prop_tgt/LINK_DIRECTORIES.rst new file mode 100644 index 0000000..085a701 --- /dev/null +++ b/Help/prop_tgt/LINK_DIRECTORIES.rst @@ -0,0 +1,18 @@ +LINK_DIRECTORIES +---------------- + +List of directories to use for the link step of shared library, module +and executable targets. + +This property holds a :ref:`;-list <CMake Language Lists>` of directories +specified so far for its target. Use the :command:`target_link_directories` +command to append more search directories. + +This property is initialized by the :prop_dir:`LINK_DIRECTORIES` directory +property when a target is created, and is used by the generators to set +the search directories for the linker. + +Contents of ``LINK_DIRECTORIES`` may use "generator expressions" with the +syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual +for available expressions. See the :manual:`cmake-buildsystem(7)` manual +for more on defining buildsystem properties. diff --git a/Help/release/dev/FindMatlab-2018b.rst b/Help/release/dev/FindMatlab-2018b.rst new file mode 100644 index 0000000..c698b9d --- /dev/null +++ b/Help/release/dev/FindMatlab-2018b.rst @@ -0,0 +1,12 @@ +FindMatlab-2018b +---------------- + +* The :module:`FindMatlab` module gained new components ``ENGINE_LIBRARY`` and + ``DATAARRAY_LIBRARY`` to request finding the Matlab C++ Engine and DataArray + libraries respectively. + +* The :module:`FindMatlab` module now explicitly exports mexFunction in Visual + Studio. + +* The :module:`FindMatlab` module gained the ability to discover Matlab R2018a + and R2018b. diff --git a/Help/release/dev/LINK_DIRECTORIES-policy.rst b/Help/release/dev/LINK_DIRECTORIES-policy.rst new file mode 100644 index 0000000..5bbfa51 --- /dev/null +++ b/Help/release/dev/LINK_DIRECTORIES-policy.rst @@ -0,0 +1,5 @@ +LINK_DIRECTORIES-policy +----------------------- + +* The :prop_tgt:`LINK_DIRECTORIES` target property expects absolute paths. + See policy :policy:`CMP0081`. diff --git a/Help/release/dev/LINK_DIRECTORIES.rst b/Help/release/dev/LINK_DIRECTORIES.rst new file mode 100644 index 0000000..dc7d609 --- /dev/null +++ b/Help/release/dev/LINK_DIRECTORIES.rst @@ -0,0 +1,9 @@ +LINK_DIRECTORIES +---------------- + +* CMake gained new capabilities to manage link directories: + + * :prop_tgt:`LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DIRECTORIES` + target properties. + * :command:`target_link_directories` command to add link directories to + targets. diff --git a/Help/release/dev/cpack-deb-dbgsym-ddeb.rst b/Help/release/dev/cpack-deb-dbgsym-ddeb.rst new file mode 100644 index 0000000..dc3e96e --- /dev/null +++ b/Help/release/dev/cpack-deb-dbgsym-ddeb.rst @@ -0,0 +1,6 @@ +cpack-deb-dbgsym-ddeb +--------------------- + +* The :cpack_gen:`CPack Deb Generator` learned to split debug symbols into + a corresponding .ddeb package when ``CPACK_DEBIAN_DEBUGINFO_PACKAGE`` is + set. diff --git a/Help/release/dev/link_directories-enhancements.rst b/Help/release/dev/link_directories-enhancements.rst new file mode 100644 index 0000000..2521fa1 --- /dev/null +++ b/Help/release/dev/link_directories-enhancements.rst @@ -0,0 +1,5 @@ +link_directories-enhancements +----------------------------- + +* :command:`link_directories` command gains capability to control directories + insertion position. diff --git a/Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst b/Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst new file mode 100644 index 0000000..026ca35 --- /dev/null +++ b/Help/variable/CMAKE_LINK_DIRECTORIES_BEFORE.rst @@ -0,0 +1,9 @@ +CMAKE_LINK_DIRECTORIES_BEFORE +----------------------------- + +Whether to append or prepend directories by default in +:command:`link_directories`. + +This variable affects the default behavior of the :command:`link_directories` +command. Setting this variable to ``ON`` is equivalent to using the ``BEFORE`` +option in all uses of that command. diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake index 24048ed..45dea8f 100644 --- a/Modules/CMakeDetermineASMCompiler.cmake +++ b/Modules/CMakeDetermineASMCompiler.cmake @@ -22,11 +22,9 @@ if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER) if("ASM${ASM_DIALECT}" STREQUAL "ASM") # the generic assembler support if(NOT CMAKE_ASM_COMPILER_INIT) if(CMAKE_C_COMPILER) - set(CMAKE_ASM_COMPILER "${CMAKE_C_COMPILER}" CACHE FILEPATH "The ASM compiler") - set(CMAKE_ASM_COMPILER_ID "${CMAKE_C_COMPILER_ID}") + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_LIST ${CMAKE_C_COMPILER}) elseif(CMAKE_CXX_COMPILER) - set(CMAKE_ASM_COMPILER "${CMAKE_CXX_COMPILER}" CACHE FILEPATH "The ASM compiler") - set(CMAKE_ASM_COMPILER_ID "${CMAKE_CXX_COMPILER_ID}") + set(CMAKE_ASM${ASM_DIALECT}_COMPILER_LIST ${CMAKE_CXX_COMPILER}) else() # List all default C and CXX compilers set(CMAKE_ASM${ASM_DIALECT}_COMPILER_LIST diff --git a/Modules/FindGLUT.cmake b/Modules/FindGLUT.cmake index 88d4b29..1779683 100644 --- a/Modules/FindGLUT.cmake +++ b/Modules/FindGLUT.cmake @@ -34,20 +34,30 @@ # GLUT_Xmu_LIBRARY = the full path to the Xmu library. # GLUT_Xi_LIBRARY = the full path to the Xi Library. +include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake) + if (WIN32) find_path( GLUT_INCLUDE_DIR NAMES GL/glut.h PATHS ${GLUT_ROOT_PATH}/include ) - find_library( GLUT_glut_LIBRARY NAMES glut glut32 freeglut + find_library( GLUT_glut_LIBRARY_RELEASE NAMES glut glut32 freeglut PATHS ${OPENGL_LIBRARY_DIR} ${GLUT_ROOT_PATH}/Release ) + find_library( GLUT_glut_LIBRARY_DEBUG NAMES freeglutd + PATHS + ${OPENGL_LIBRARY_DIR} + ${GLUT_ROOT_PATH}/Debug + ) + mark_as_advanced(GLUT_glut_LIBRARY_RELEASE GLUT_glut_LIBRARY_DEBUG) + select_library_configurations(GLUT_glut) else () if (APPLE) find_path(GLUT_INCLUDE_DIR glut.h ${OPENGL_LIBRARY_DIR}) find_library(GLUT_glut_LIBRARY GLUT DOC "GLUT library for OSX") find_library(GLUT_cocoa_LIBRARY Cocoa DOC "Cocoa framework for OSX") + mark_as_advanced(GLUT_glut_LIBRARY GLUT_cocoa_LIBRARY) if(GLUT_cocoa_LIBRARY AND NOT TARGET GLUT::Cocoa) add_library(GLUT::Cocoa UNKNOWN IMPORTED) @@ -72,10 +82,12 @@ else () find_library( GLUT_Xi_LIBRARY Xi /usr/openwin/lib ) + mark_as_advanced(GLUT_Xi_LIBRARY) find_library( GLUT_Xmu_LIBRARY Xmu /usr/openwin/lib ) + mark_as_advanced(GLUT_Xmu_LIBRARY) if(GLUT_Xi_LIBRARY AND NOT TARGET GLUT::Xi) add_library(GLUT::Xi UNKNOWN IMPORTED) @@ -104,6 +116,7 @@ else () /usr/openwin/lib ${_GLUT_glut_LIB_DIR} ) + mark_as_advanced(GLUT_glut_LIBRARY) unset(_GLUT_INC_DIR) unset(_GLUT_glut_LIB_DIR) @@ -135,8 +148,24 @@ if (GLUT_FOUND) set_target_properties(GLUT::GLUT PROPERTIES IMPORTED_LOCATION "${GLUT_glut_LIBRARY}/${CMAKE_MATCH_1}") else() - set_target_properties(GLUT::GLUT PROPERTIES - IMPORTED_LOCATION "${GLUT_glut_LIBRARY}") + if(GLUT_glut_LIBRARY_RELEASE) + set_property(TARGET GLUT::GLUT APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(GLUT::GLUT PROPERTIES + IMPORTED_LOCATION_RELEASE "${GLUT_glut_LIBRARY_RELEASE}") + endif() + + if(GLUT_glut_LIBRARY_DEBUG) + set_property(TARGET GLUT::GLUT APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(GLUT::GLUT PROPERTIES + IMPORTED_LOCATION_DEBUG "${GLUT_glut_LIBRARY_DEBUG}") + endif() + + if(NOT GLUT_glut_LIBRARY_RELEASE AND NOT GLUT_glut_LIBRARY_DEBUG) + set_property(TARGET GLUT::GLUT APPEND PROPERTY + IMPORTED_LOCATION "${GLUT_glut_LIBRARY}") + endif() endif() if(TARGET GLUT::Xmu) @@ -160,9 +189,4 @@ if (GLUT_FOUND) set (GLUT_INCLUDE_PATH ${GLUT_INCLUDE_DIR}) endif() -mark_as_advanced( - GLUT_INCLUDE_DIR - GLUT_glut_LIBRARY - GLUT_Xmu_LIBRARY - GLUT_Xi_LIBRARY - ) +mark_as_advanced(GLUT_INCLUDE_DIR) diff --git a/Modules/FindLua.cmake b/Modules/FindLua.cmake index e86c15c..68530b3 100644 --- a/Modules/FindLua.cmake +++ b/Modules/FindLua.cmake @@ -198,6 +198,7 @@ endif () find_library(LUA_LIBRARY NAMES ${_lua_library_names} lua + NAMES_PER_DIR HINTS ENV LUA_DIR PATH_SUFFIXES lib diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake index 12a2f75..547a330 100644 --- a/Modules/FindMatlab.cmake +++ b/Modules/FindMatlab.cmake @@ -21,6 +21,8 @@ # # * ``MX_LIBRARY``, ``ENG_LIBRARY`` and ``MAT_LIBRARY``: respectively the ``MX``, # ``ENG`` and ``MAT`` libraries of Matlab +# * ``ENGINE_LIBRARY``, ``DATAARRAY_LIBRARY``: respectively the ``MatlabEngine`` +# and ``MatlabDataArray`` libraries of Matlab (Matlab 2018a and later) # * ``MAIN_PROGRAM`` the Matlab binary program. Note that this component is not # available on the MCR version, and will yield an error if the MCR is found # instead of the regular Matlab installation. @@ -107,6 +109,12 @@ # ``Matlab_MAT_LIBRARY`` # Matlab matrix library. Available only if the component ``MAT_LIBRARY`` # is requested. +# ``Matlab_ENGINE_LIBRARY`` +# Matlab C++ engine library. Available only if the component ``ENGINE_LIBRARY`` +# is requested. +# ``Matlab_DATAARRAY_LIBRARY`` +# Matlab C++ data array library. Available only if the component ``DATAARRAY_LIBRARY`` +# is requested. # ``Matlab_LIBRARIES`` # the whole set of libraries of Matlab # ``Matlab_MEX_COMPILER`` @@ -234,6 +242,8 @@ if(NOT MATLAB_ADDITIONAL_VERSIONS) endif() set(MATLAB_VERSIONS_MAPPING + "R2018b=9.5" + "R2018a=9.4" "R2017b=9.3" "R2017a=9.2" "R2016b=9.1" @@ -960,6 +970,14 @@ function(matlab_add_mex) target_link_libraries(${${prefix}_NAME} ${Matlab_MX_LIBRARY}) endif() + if(DEFINED Matlab_ENGINE_LIBRARY) + target_link_libraries(${${prefix}_NAME} ${Matlab_ENGINE_LIBRARY}) + endif() + + if(DEFINED Matlab_DATAARRAY_LIBRARY) + target_link_libraries(${${prefix}_NAME} ${Matlab_DATAARRAY_LIBRARY}) + endif() + target_link_libraries(${${prefix}_NAME} ${Matlab_MEX_LIBRARY} ${${prefix}_LINK_TO}) set_target_properties(${${prefix}_NAME} PROPERTIES @@ -980,6 +998,20 @@ function(matlab_add_mex) endif() # documentation # entry point in the mex file + taking care of visibility and symbol clashes. + if (MSVC) + get_target_property( + _previous_link_flags + ${${prefix}_NAME} + LINK_FLAGS) + if(NOT _previous_link_flags) + set(_previous_link_flags) + endif() + + set_target_properties(${${prefix}_NAME} + PROPERTIES + LINK_FLAGS "${_previous_link_flags} /EXPORT:mexFunction") + endif() + if(WIN32) set_target_properties(${${prefix}_NAME} PROPERTIES @@ -1416,6 +1448,8 @@ if(DEFINED Matlab_ROOT_DIR_LAST_CACHED) Matlab_MX_LIBRARY Matlab_ENG_LIBRARY Matlab_MAT_LIBRARY + Matlab_ENGINE_LIBRARY + Matlab_DATAARRAY_LIBRARY Matlab_MEX_EXTENSION Matlab_SIMULINK_INCLUDE_DIR @@ -1668,10 +1702,52 @@ if(_matlab_find_mcc_compiler GREATER -1) endif() unset(_matlab_find_mcc_compiler) +# component MatlabEngine +list(FIND Matlab_FIND_COMPONENTS ENGINE_LIBRARY _matlab_find_matlab_engine) +if(_matlab_find_matlab_engine GREATER -1) + _Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_ENGINE_LIBRARY + MatlabEngine + PATHS ${_matlab_lib_dir_for_search} + DOC "MatlabEngine Library" + NO_DEFAULT_PATH + ) + if(Matlab_ENGINE_LIBRARY) + set(Matlab_ENGINE_LIBRARY_FOUND TRUE) + endif() +endif() +unset(_matlab_find_matlab_engine) + +# component MatlabDataArray +list(FIND Matlab_FIND_COMPONENTS DATAARRAY_LIBRARY _matlab_find_matlab_dataarray) +if(_matlab_find_matlab_dataarray GREATER -1) + _Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_DATAARRAY_LIBRARY + MatlabDataArray + PATHS ${_matlab_lib_dir_for_search} + DOC "MatlabDataArray Library" + NO_DEFAULT_PATH + ) + if(Matlab_DATAARRAY_LIBRARY) + set(Matlab_DATAARRAY_LIBRARY_FOUND TRUE) + endif() +endif() +unset(_matlab_find_matlab_dataarray) + unset(_matlab_lib_dir_for_search) set(Matlab_LIBRARIES ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY} ${Matlab_ENG_LIBRARY} ${Matlab_MAT_LIBRARY}) +if(Matlab_DATAARRAY_LIBRARY_FOUND) + set(Matlab_LIBRARIES ${Matlab_LIBRARIES} ${Matlab_DATAARRAY_LIBRARY}) +endif() + +if(Matlab_ENGINE_LIBRARY_FOUND) + set(Matlab_LIBRARIES ${Matlab_LIBRARIES} ${Matlab_ENGINE_LIBRARY}) +endif() + find_package_handle_standard_args( Matlab FOUND_VAR Matlab_FOUND @@ -1692,6 +1768,8 @@ if(Matlab_INCLUDE_DIRS AND Matlab_LIBRARIES) Matlab_MEX_LIBRARY Matlab_MX_LIBRARY Matlab_ENG_LIBRARY + Matlab_ENGINE_LIBRARY + Matlab_DATAARRAY_LIBRARY Matlab_MAT_LIBRARY Matlab_INCLUDE_DIRS Matlab_FOUND diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake index ca3a004..3042a16 100644 --- a/Modules/Internal/CPack/CPackDeb.cmake +++ b/Modules/Internal/CPack/CPackDeb.cmake @@ -64,6 +64,8 @@ function(cpack_deb_prepare_package_vars) endif() set(WDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}") + set(DBGSYMDIR "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_DEB_PACKAGE_COMPONENT_PART_PATH}-dbgsym") + file(REMOVE_RECURSE "${DBGSYMDIR}") # per component automatic discover: some of the component might not have # binaries. @@ -80,7 +82,10 @@ function(cpack_deb_prepare_package_vars) endif() endif() - if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OR CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS) + cpack_deb_variable_fallback("CPACK_DEBIAN_DEBUGINFO_PACKAGE" + "CPACK_DEBIAN_${_local_component_name}_DEBUGINFO_PACKAGE" + "CPACK_DEBIAN_DEBUGINFO_PACKAGE") + if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OR CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS OR CPACK_DEBIAN_DEBUGINFO_PACKAGE) # Generating binary list - Get type of all install files file(GLOB_RECURSE FILE_PATHS_ LIST_DIRECTORIES false RELATIVE "${WDIR}" "${WDIR}/*") @@ -92,7 +97,7 @@ function(cpack_deb_prepare_package_vars) # get file info so that we can determine if file is executable or not unset(CPACK_DEB_INSTALL_FILES) foreach(FILE_ IN LISTS FILE_PATHS_) - execute_process(COMMAND env LC_ALL=C ${FILE_EXECUTABLE} "./${FILE_}" + execute_process(COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${FILE_EXECUTABLE} "./${FILE_}" WORKING_DIRECTORY "${WDIR}" RESULT_VARIABLE FILE_RESULT_ OUTPUT_VARIABLE INSTALL_FILE_) @@ -114,6 +119,81 @@ function(cpack_deb_prepare_package_vars) string(REGEX MATCH "(^.*):" _FILE_NAME "${_FILE}") list(APPEND CPACK_DEB_SHARED_OBJECT_FILES "${CMAKE_MATCH_1}") endif() + if(_FILE MATCHES "ELF.*not stripped") + string(REGEX MATCH "(^.*):" _FILE_NAME "${_FILE}") + list(APPEND CPACK_DEB_UNSTRIPPED_FILES "${CMAKE_MATCH_1}") + endif() + endforeach() + endif() + + find_program(READELF_EXECUTABLE NAMES readelf) + + if(CPACK_DEBIAN_DEBUGINFO_PACKAGE AND CPACK_DEB_UNSTRIPPED_FILES) + find_program(OBJCOPY_EXECUTABLE NAMES objcopy) + + if(NOT OBJCOPY_EXECUTABLE) + message(FATAL_ERROR "debuginfo packages require the objcopy tool") + endif() + if(NOT READELF_EXECUTABLE) + message(FATAL_ERROR "debuginfo packages require the readelf tool") + endif() + + file(RELATIVE_PATH _DBGSYM_ROOT "${CPACK_TEMPORARY_DIRECTORY}" "${DBGSYMDIR}") + foreach(_FILE IN LISTS CPACK_DEB_UNSTRIPPED_FILES) + + # Get the file's Build ID + execute_process(COMMAND env LC_ALL=C ${READELF_EXECUTABLE} -n "${_FILE}" + WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + OUTPUT_VARIABLE READELF_OUTPUT + RESULT_VARIABLE READELF_RESULT + ERROR_VARIABLE READELF_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE ) + if(NOT READELF_RESULT EQUAL 0) + message(FATAL_ERROR "CPackDeb: readelf: '${READELF_ERROR}';\n" + "executed command: '${READELF_EXECUTABLE} -n ${_FILE}'") + endif() + if(READELF_OUTPUT MATCHES "Build ID: ([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z]*)") + set(_BUILD_ID_START ${CMAKE_MATCH_1}) + set(_BUILD_ID_REMAINING ${CMAKE_MATCH_2}) + list(APPEND BUILD_IDS ${_BUILD_ID_START}${_BUILD_ID_REMAINING}) + else() + message(FATAL_ERROR "Unable to determine Build ID for ${_FILE}") + endif() + + # Split out the debug symbols from the binaries + set(_FILE_DBGSYM ${_DBGSYM_ROOT}/usr/lib/debug/.build-id/${_BUILD_ID_START}/${_BUILD_ID_REMAINING}.debug) + get_filename_component(_OUT_DIR "${_FILE_DBGSYM}" DIRECTORY) + file(MAKE_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}/${_OUT_DIR}") + execute_process(COMMAND ${OBJCOPY_EXECUTABLE} --only-keep-debug "${_FILE}" "${_FILE_DBGSYM}" + WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + OUTPUT_VARIABLE OBJCOPY_OUTPUT + RESULT_VARIABLE OBJCOPY_RESULT + ERROR_VARIABLE OBJCOPY_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE ) + if(NOT OBJCOPY_RESULT EQUAL 0) + message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" + "executed command: '${OBJCOPY_EXECUTABLE} --only-keep-debug ${_FILE} ${_FILE_DBGSYM}'") + endif() + execute_process(COMMAND ${OBJCOPY_EXECUTABLE} --strip-unneeded ${_FILE} + WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + OUTPUT_VARIABLE OBJCOPY_OUTPUT + RESULT_VARIABLE OBJCOPY_RESULT + ERROR_VARIABLE OBJCOPY_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE ) + if(NOT OBJCOPY_RESULT EQUAL 0) + message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" + "executed command: '${OBJCOPY_EXECUTABLE} --strip-debug ${_FILE}'") + endif() + execute_process(COMMAND ${OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE} + WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}" + OUTPUT_VARIABLE OBJCOPY_OUTPUT + RESULT_VARIABLE OBJCOPY_RESULT + ERROR_VARIABLE OBJCOPY_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE ) + if(NOT OBJCOPY_RESULT EQUAL 0) + message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n" + "executed command: '${OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE}'") + endif() endforeach() endif() @@ -123,7 +203,7 @@ function(cpack_deb_prepare_package_vars) if(SHLIBDEPS_EXECUTABLE) # Check version of the dpkg-shlibdeps tool using CPackDEB method - execute_process(COMMAND env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --version + execute_process(COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --version OUTPUT_VARIABLE _TMP_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -151,7 +231,7 @@ function(cpack_deb_prepare_package_vars) file(MAKE_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}/DEBIAN") # Add --ignore-missing-info if the tool supports it - execute_process(COMMAND env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --help + execute_process(COMMAND ${CMAKE_COMMAND} -E env LC_ALL=C ${SHLIBDEPS_EXECUTABLE} --help OUTPUT_VARIABLE _TMP_HELP ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -450,8 +530,6 @@ function(cpack_deb_prepare_package_vars) set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY "=") endif() - find_program(READELF_EXECUTABLE NAMES readelf) - if(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS) if(READELF_EXECUTABLE) foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES) @@ -507,18 +585,24 @@ function(cpack_deb_prepare_package_vars) # <foo>_<VersionNumber>-<DebianRevisionNumber>_<DebianArchitecture>.deb set(CPACK_OUTPUT_FILE_NAME "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") + set(CPACK_DBGSYM_OUTPUT_FILE_NAME + "${CPACK_DEBIAN_PACKAGE_NAME}-dbgsym_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.ddeb") else() if(NOT CPACK_DEBIAN_FILE_NAME MATCHES ".*\\.(deb|ipk)") message(FATAL_ERROR "'${CPACK_DEBIAN_FILE_NAME}' is not a valid DEB package file name as it must end with '.deb' or '.ipk'!") endif() set(CPACK_OUTPUT_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}") + string(REGEX REPLACE "\.deb$" "-dbgsym.ddeb" CPACK_DBGSYM_OUTPUT_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}") endif() set(CPACK_TEMPORARY_PACKAGE_FILE_NAME "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_OUTPUT_FILE_NAME}") get_filename_component(BINARY_DIR "${CPACK_OUTPUT_FILE_PATH}" DIRECTORY) set(CPACK_OUTPUT_FILE_PATH "${BINARY_DIR}/${CPACK_OUTPUT_FILE_NAME}") - endif() # else() back compatibility - don't change the name + else() + # back compatibility - don't change the name + string(REGEX REPLACE "\.deb$" "-dbgsym.ddeb" CPACK_DBGSYM_OUTPUT_FILE_NAME "${CPACK_OUTPUT_FILE_NAME}") + endif() # Print out some debug information if we were asked for that if(CPACK_DEBIAN_PACKAGE_DEBUG) @@ -579,6 +663,14 @@ function(cpack_deb_prepare_package_vars) set(GEN_CPACK_DEBIAN_GENERATE_POSTINST "${CPACK_DEBIAN_GENERATE_POSTINST}" PARENT_SCOPE) set(GEN_CPACK_DEBIAN_GENERATE_POSTRM "${CPACK_DEBIAN_GENERATE_POSTRM}" PARENT_SCOPE) set(GEN_WDIR "${WDIR}" PARENT_SCOPE) + + set(GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE "${CPACK_DEBIAN_DEBUGINFO_PACKAGE}" PARENT_SCOPE) + if(BUILD_IDS) + set(GEN_DBGSYMDIR "${DBGSYMDIR}" PARENT_SCOPE) + set(GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME "${CPACK_DBGSYM_OUTPUT_FILE_NAME}" PARENT_SCOPE) + string(REPLACE ";" " " BUILD_IDS "${BUILD_IDS}") + set(GEN_BUILD_IDS "${BUILD_IDS}" PARENT_SCOPE) + endif() endfunction() cpack_deb_prepare_package_vars() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 628cc6f..3cf6c8f 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -579,6 +579,8 @@ set(SRCS cmTargetIncludeDirectoriesCommand.h cmTargetLinkOptionsCommand.cxx cmTargetLinkOptionsCommand.h + cmTargetLinkDirectoriesCommand.cxx + cmTargetLinkDirectoriesCommand.h cmTargetLinkLibrariesCommand.cxx cmTargetLinkLibrariesCommand.h cmTargetPropCommandBase.cxx @@ -1047,6 +1049,9 @@ target_link_libraries(cmake CMakeLib) add_library(CMakeServerLib cmConnection.h cmConnection.cxx cmFileMonitor.cxx cmFileMonitor.h + cmJsonObjectDictionary.h + cmJsonObjects.h + cmJsonObjects.cxx cmPipeConnection.cxx cmPipeConnection.h cmServer.cxx cmServer.h cmServerConnection.cxx cmServerConnection.h diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 22d519f..d9aa818 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 12) -set(CMake_VERSION_PATCH 20180921) +set(CMake_VERSION_PATCH 20180928) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 972fe6f..ea0ee58 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -12,11 +12,430 @@ #include "cm_sys_stat.h" #include "cmsys/Glob.hxx" +#include <map> #include <ostream> #include <set> #include <string.h> #include <utility> +namespace { + +class DebGenerator +{ +public: + DebGenerator(cmCPackLog* logger, std::string const& outputName, + std::string const& workDir, std::string const& topLevelDir, + std::string const& temporaryDir, + const char* debianCompressionType, + const char* debianArchiveType, + std::map<std::string, std::string> const& controlValues, + bool genShLibs, std::string const& shLibsFilename, + bool genPostInst, std::string const& postInst, bool genPostRm, + std::string const& postRm, const char* controlExtra, + bool permissionStrctPolicy, + std::vector<std::string> const& packageFiles); + + bool generate() const; + +private: + void generateDebianBinaryFile() const; + void generateControlFile() const; + bool generateDataTar() const; + std::string generateMD5File() const; + bool generateControlTar(std::string const& md5Filename) const; + bool generateDeb() const; + + cmCPackLog* Logger; + const std::string OutputName; + const std::string WorkDir; + std::string CompressionSuffix; + const std::string TopLevelDir; + const std::string TemporaryDir; + const char* DebianArchiveType; + const std::map<std::string, std::string> ControlValues; + const bool GenShLibs; + const std::string ShLibsFilename; + const bool GenPostInst; + const std::string PostInst; + const bool GenPostRm; + const std::string PostRm; + const char* ControlExtra; + const bool PermissionStrictPolicy; + const std::vector<std::string> PackageFiles; + cmArchiveWrite::Compress TarCompressionType; +}; + +DebGenerator::DebGenerator( + cmCPackLog* logger, std::string const& outputName, + std::string const& workDir, std::string const& topLevelDir, + std::string const& temporaryDir, const char* debianCompressionType, + const char* debianArchiveType, + std::map<std::string, std::string> const& controlValues, bool genShLibs, + std::string const& shLibsFilename, bool genPostInst, + std::string const& postInst, bool genPostRm, std::string const& postRm, + const char* controlExtra, bool permissionStrictPolicy, + std::vector<std::string> const& packageFiles) + : Logger(logger) + , OutputName(outputName) + , WorkDir(workDir) + , TopLevelDir(topLevelDir) + , TemporaryDir(temporaryDir) + , DebianArchiveType(debianArchiveType ? debianArchiveType : "paxr") + , ControlValues(controlValues) + , GenShLibs(genShLibs) + , ShLibsFilename(shLibsFilename) + , GenPostInst(genPostInst) + , PostInst(postInst) + , GenPostRm(genPostRm) + , PostRm(postRm) + , ControlExtra(controlExtra) + , PermissionStrictPolicy(permissionStrictPolicy) + , PackageFiles(packageFiles) +{ + if (!debianCompressionType) { + debianCompressionType = "gzip"; + } + + if (!strcmp(debianCompressionType, "lzma")) { + CompressionSuffix = ".lzma"; + TarCompressionType = cmArchiveWrite::CompressLZMA; + } else if (!strcmp(debianCompressionType, "xz")) { + CompressionSuffix = ".xz"; + TarCompressionType = cmArchiveWrite::CompressXZ; + } else if (!strcmp(debianCompressionType, "bzip2")) { + CompressionSuffix = ".bz2"; + TarCompressionType = cmArchiveWrite::CompressBZip2; + } else if (!strcmp(debianCompressionType, "gzip")) { + CompressionSuffix = ".gz"; + TarCompressionType = cmArchiveWrite::CompressGZip; + } else if (!strcmp(debianCompressionType, "none")) { + CompressionSuffix.clear(); + TarCompressionType = cmArchiveWrite::CompressNone; + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error unrecognized compression type: " + << debianCompressionType << std::endl); + } +} + +bool DebGenerator::generate() const +{ + generateDebianBinaryFile(); + generateControlFile(); + if (!generateDataTar()) { + return false; + } + std::string md5Filename = generateMD5File(); + if (!generateControlTar(md5Filename)) { + return false; + } + return generateDeb(); +} + +void DebGenerator::generateDebianBinaryFile() const +{ + // debian-binary file + const std::string dbfilename = WorkDir + "/debian-binary"; + cmGeneratedFileStream out(dbfilename); + out << "2.0"; + out << std::endl; // required for valid debian package +} + +void DebGenerator::generateControlFile() const +{ + std::string ctlfilename = WorkDir + "/control"; + + cmGeneratedFileStream out(ctlfilename); + for (auto const& kv : ControlValues) { + out << kv.first << ": " << kv.second << "\n"; + } + + unsigned long totalSize = 0; + { + std::string dirName = TemporaryDir; + dirName += '/'; + for (std::string const& file : PackageFiles) { + totalSize += cmSystemTools::FileLength(file); + } + } + out << "Installed-Size: " << (totalSize + 1023) / 1024 << "\n"; + out << std::endl; +} + +bool DebGenerator::generateDataTar() const +{ + std::string filename_data_tar = WorkDir + "/data.tar" + CompressionSuffix; + cmGeneratedFileStream fileStream_data_tar; + fileStream_data_tar.Open(filename_data_tar, false, true); + if (!fileStream_data_tar) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error opening the file \"" + << filename_data_tar << "\" for writing" << std::endl); + return false; + } + cmArchiveWrite data_tar(fileStream_data_tar, TarCompressionType, + DebianArchiveType); + + // uid/gid should be the one of the root user, and this root user has + // always uid/gid equal to 0. + data_tar.SetUIDAndGID(0u, 0u); + data_tar.SetUNAMEAndGNAME("root", "root"); + + // now add all directories which have to be compressed + // collect all top level install dirs for that + // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would + // give /usr and /opt + size_t topLevelLength = WorkDir.length(); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "WDIR: \"" << WorkDir << "\", length = " << topLevelLength + << std::endl); + std::set<std::string> orderedFiles; + + // we have to reconstruct the parent folders as well + + for (std::string currentPath : PackageFiles) { + while (currentPath != WorkDir) { + // the last one IS WorkDir, but we do not want this one: + // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application + // should not add XXX/application + orderedFiles.insert(currentPath); + currentPath = cmSystemTools::CollapseCombinedPath(currentPath, ".."); + } + } + + for (std::string const& file : orderedFiles) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "FILEIT: \"" << file << "\"" << std::endl); + std::string::size_type slashPos = file.find('/', topLevelLength + 1); + std::string relativeDir = + file.substr(topLevelLength, slashPos - topLevelLength); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "RELATIVEDIR: \"" << relativeDir << "\"" << std::endl); + +#ifdef WIN32 + std::string mode_t_adt_filename = file + ":cmake_mode_t"; + cmsys::ifstream permissionStream(mode_t_adt_filename.c_str()); + + mode_t permissions = 0; + + if (permissionStream) { + permissionStream >> std::oct >> permissions; + } + + if (permissions != 0) { + data_tar.SetPermissions(permissions); + } else if (cmSystemTools::FileIsDirectory(file)) { + data_tar.SetPermissions(0755); + } else { + data_tar.ClearPermissions(); + } +#endif + + // do not recurse because the loop will do it + if (!data_tar.Add(file, topLevelLength, ".", false)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: " << file << std::endl + << "#error:" << data_tar.GetError() << std::endl); + return false; + } + } + return true; +} + +std::string DebGenerator::generateMD5File() const +{ + std::string md5filename = WorkDir + "/md5sums"; + + cmGeneratedFileStream out(md5filename); + + std::string topLevelWithTrailingSlash = TemporaryDir; + topLevelWithTrailingSlash += '/'; + for (std::string const& file : PackageFiles) { + // hash only regular files + if (cmSystemTools::FileIsDirectory(file) || + cmSystemTools::FileIsSymlink(file)) { + continue; + } + + std::string output = + cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5); + if (output.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem computing the md5 of " << file << std::endl); + } + + output += " " + file + "\n"; + // debian md5sums entries are like this: + // 014f3604694729f3bf19263bac599765 usr/bin/ccmake + // thus strip the full path (with the trailing slash) + cmSystemTools::ReplaceString(output, topLevelWithTrailingSlash.c_str(), + ""); + out << output; + } + // each line contains a eol. + // Do not end the md5sum file with yet another (invalid) + return md5filename; +} + +bool DebGenerator::generateControlTar(std::string const& md5Filename) const +{ + std::string filename_control_tar = WorkDir + "/control.tar.gz"; + + cmGeneratedFileStream fileStream_control_tar; + fileStream_control_tar.Open(filename_control_tar, false, true); + if (!fileStream_control_tar) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error opening the file \"" + << filename_control_tar << "\" for writing" << std::endl); + return false; + } + cmArchiveWrite control_tar(fileStream_control_tar, + cmArchiveWrite::CompressGZip, DebianArchiveType); + + // sets permissions and uid/gid for the files + control_tar.SetUIDAndGID(0u, 0u); + control_tar.SetUNAMEAndGNAME("root", "root"); + + /* permissions are set according to + https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners + and + https://lintian.debian.org/tags/control-file-has-bad-permissions.html + */ + const mode_t permission644 = 0644; + const mode_t permissionExecute = 0111; + const mode_t permission755 = permission644 | permissionExecute; + + // for md5sum and control (that we have generated here), we use 644 + // (RW-R--R--) + // so that deb lintian doesn't warn about it + control_tar.SetPermissions(permission644); + + // adds control and md5sums + if (!control_tar.Add(md5Filename, WorkDir.length(), ".") || + !control_tar.Add(WorkDir + "/control", WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"control\" or \"md5sums\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + + // adds generated shlibs file + if (GenShLibs) { + if (!control_tar.Add(ShLibsFilename, WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"shlibs\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + } + + // adds LDCONFIG related files + if (GenPostInst) { + control_tar.SetPermissions(permission755); + if (!control_tar.Add(PostInst, WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"postinst\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + control_tar.SetPermissions(permission644); + } + + if (GenPostRm) { + control_tar.SetPermissions(permission755); + if (!control_tar.Add(PostRm, WorkDir.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error adding file to tar:" + << std::endl + << "#top level directory: " << WorkDir << std::endl + << "#file: \"postinst\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return false; + } + control_tar.SetPermissions(permission644); + } + + // for the other files, we use + // -either the original permission on the files + // -either a permission strictly defined by the Debian policies + if (ControlExtra) { + // permissions are now controlled by the original file permissions + + static const char* strictFiles[] = { "config", "postinst", "postrm", + "preinst", "prerm" }; + std::set<std::string> setStrictFiles( + strictFiles, strictFiles + sizeof(strictFiles) / sizeof(strictFiles[0])); + + // default + control_tar.ClearPermissions(); + + std::vector<std::string> controlExtraList; + cmSystemTools::ExpandListArgument(ControlExtra, controlExtraList); + for (std::string const& i : controlExtraList) { + std::string filenamename = cmsys::SystemTools::GetFilenameName(i); + std::string localcopy = WorkDir + "/" + filenamename; + + if (PermissionStrictPolicy) { + control_tar.SetPermissions( + setStrictFiles.count(filenamename) ? permission755 : permission644); + } + + // if we can copy the file, it means it does exist, let's add it: + if (cmsys::SystemTools::CopyFileIfDifferent(i, localcopy)) { + control_tar.Add(localcopy, WorkDir.length(), "."); + } + } + } + + return true; +} + +bool DebGenerator::generateDeb() const +{ + // ar -r your-package-name.deb debian-binary control.tar.* data.tar.* + // A debian package .deb is simply an 'ar' archive. The only subtle + // difference is that debian uses the BSD ar style archive whereas most + // Linux distro have a GNU ar. + // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info + std::string const outputPath = TopLevelDir + "/" + OutputName; + std::string const tlDir = WorkDir + "/"; + cmGeneratedFileStream debStream; + debStream.Open(outputPath, false, true); + cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd"); + + // uid/gid should be the one of the root user, and this root user has + // always uid/gid equal to 0. + deb.SetUIDAndGID(0u, 0u); + deb.SetUNAMEAndGNAME("root", "root"); + + if (!deb.Add(tlDir + "debian-binary", tlDir.length()) || + !deb.Add(tlDir + "control.tar.gz", tlDir.length()) || + !deb.Add(tlDir + "data.tar" + CompressionSuffix, tlDir.length())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error creating debian package:" + << std::endl + << "#top level directory: " << TopLevelDir << std::endl + << "#file: " << OutputName << std::endl + << "#error:" << deb.GetError() << std::endl); + return false; + } + return true; +} + +} // end anonymous namespace + cmCPackDebGenerator::cmCPackDebGenerator() { } @@ -68,18 +487,20 @@ int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel, return retval; } - cmsys::Glob gl; - std::string findExpr(this->GetOption("GEN_WDIR")); - findExpr += "/*"; - gl.RecurseOn(); - gl.SetRecurseListDirs(true); - if (!gl.FindFiles(findExpr)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Cannot find any files in the installed directory" - << std::endl); - return 0; + { // Isolate globbing of binaries vs. dbgsyms + cmsys::Glob gl; + std::string findExpr(this->GetOption("GEN_WDIR")); + findExpr += "/*"; + gl.RecurseOn(); + gl.SetRecurseListDirs(true); + if (!gl.FindFiles(findExpr)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" + << std::endl); + return 0; + } + packageFiles = gl.GetFiles(); } - packageFiles = gl.GetFiles(); int res = createDeb(); if (res != 1) { @@ -90,6 +511,32 @@ int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel, packageFileName += "/"; packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"); packageFileNames.push_back(std::move(packageFileName)); + + if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE")) { + cmsys::Glob gl; + std::string findExpr(this->GetOption("GEN_DBGSYMDIR")); + findExpr += "/*"; + gl.RecurseOn(); + gl.SetRecurseListDirs(true); + if (!gl.FindFiles(findExpr)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" + << std::endl); + return 0; + } + packageFiles = gl.GetFiles(); + + res = createDbgsymDDeb(); + if (res != 1) { + retval = 0; + } + // add the generated package to package file names list + packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + packageFileName += "/"; + packageFileName += this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"); + packageFileNames.push_back(std::move(packageFileName)); + } + return retval; } @@ -234,112 +681,81 @@ int cmCPackDebGenerator::PackageFiles() int cmCPackDebGenerator::createDeb() { - // debian-binary file - const std::string strGenWDIR(this->GetOption("GEN_WDIR")); - const std::string dbfilename = strGenWDIR + "/debian-binary"; - { // the scope is needed for cmGeneratedFileStream - cmGeneratedFileStream out(dbfilename); - out << "2.0"; - out << std::endl; // required for valid debian package - } - - // control file - std::string ctlfilename = strGenWDIR + "/control"; + std::map<std::string, std::string> controlValues; // debian policy enforce lower case for package name - // mandatory entries: - std::string debian_pkg_name = cmsys::SystemTools::LowerCase( + controlValues["Package"] = cmsys::SystemTools::LowerCase( this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME")); - const char* debian_pkg_version = + controlValues["Version"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION"); - const char* debian_pkg_section = + controlValues["Section"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION"); - const char* debian_pkg_priority = + controlValues["Priority"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY"); - const char* debian_pkg_arch = + controlValues["Architecture"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE"); - const char* maintainer = + controlValues["Maintainer"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER"); - const char* desc = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION"); + controlValues["Description"] = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION"); - // optional entries + const char* debian_pkg_source = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); + if (debian_pkg_source && *debian_pkg_source) { + controlValues["Source"] = debian_pkg_source; + } const char* debian_pkg_dep = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS"); + if (debian_pkg_dep && *debian_pkg_dep) { + controlValues["Depends"] = debian_pkg_dep; + } const char* debian_pkg_rec = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS"); + if (debian_pkg_rec && *debian_pkg_rec) { + controlValues["Recommends"] = debian_pkg_rec; + } const char* debian_pkg_sug = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS"); + if (debian_pkg_sug && *debian_pkg_sug) { + controlValues["Suggests"] = debian_pkg_sug; + } const char* debian_pkg_url = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE"); + if (debian_pkg_url && *debian_pkg_url) { + controlValues["Homepage"] = debian_pkg_url; + } const char* debian_pkg_predep = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS"); + if (debian_pkg_predep && *debian_pkg_predep) { + controlValues["Pre-Depends"] = debian_pkg_predep; + } const char* debian_pkg_enhances = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES"); + if (debian_pkg_enhances && *debian_pkg_enhances) { + controlValues["Enhances"] = debian_pkg_enhances; + } const char* debian_pkg_breaks = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS"); + if (debian_pkg_breaks && *debian_pkg_breaks) { + controlValues["Breaks"] = debian_pkg_breaks; + } const char* debian_pkg_conflicts = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS"); + if (debian_pkg_conflicts && *debian_pkg_conflicts) { + controlValues["Conflicts"] = debian_pkg_conflicts; + } const char* debian_pkg_provides = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES"); + if (debian_pkg_provides && *debian_pkg_provides) { + controlValues["Provides"] = debian_pkg_provides; + } const char* debian_pkg_replaces = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_REPLACES"); - const char* debian_pkg_source = - this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); - - { // the scope is needed for cmGeneratedFileStream - cmGeneratedFileStream out(ctlfilename); - out << "Package: " << debian_pkg_name << "\n"; - out << "Version: " << debian_pkg_version << "\n"; - out << "Section: " << debian_pkg_section << "\n"; - out << "Priority: " << debian_pkg_priority << "\n"; - out << "Architecture: " << debian_pkg_arch << "\n"; - if (debian_pkg_source && *debian_pkg_source) { - out << "Source: " << debian_pkg_source << "\n"; - } - if (debian_pkg_dep && *debian_pkg_dep) { - out << "Depends: " << debian_pkg_dep << "\n"; - } - if (debian_pkg_rec && *debian_pkg_rec) { - out << "Recommends: " << debian_pkg_rec << "\n"; - } - if (debian_pkg_sug && *debian_pkg_sug) { - out << "Suggests: " << debian_pkg_sug << "\n"; - } - if (debian_pkg_url && *debian_pkg_url) { - out << "Homepage: " << debian_pkg_url << "\n"; - } - if (debian_pkg_predep && *debian_pkg_predep) { - out << "Pre-Depends: " << debian_pkg_predep << "\n"; - } - if (debian_pkg_enhances && *debian_pkg_enhances) { - out << "Enhances: " << debian_pkg_enhances << "\n"; - } - if (debian_pkg_breaks && *debian_pkg_breaks) { - out << "Breaks: " << debian_pkg_breaks << "\n"; - } - if (debian_pkg_conflicts && *debian_pkg_conflicts) { - out << "Conflicts: " << debian_pkg_conflicts << "\n"; - } - if (debian_pkg_provides && *debian_pkg_provides) { - out << "Provides: " << debian_pkg_provides << "\n"; - } - if (debian_pkg_replaces && *debian_pkg_replaces) { - out << "Replaces: " << debian_pkg_replaces << "\n"; - } - unsigned long totalSize = 0; - { - std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); - dirName += '/'; - for (std::string const& file : packageFiles) { - totalSize += cmSystemTools::FileLength(file); - } - } - out << "Installed-Size: " << (totalSize + 1023) / 1024 << "\n"; - out << "Maintainer: " << maintainer << "\n"; - out << "Description: " << desc << "\n"; - out << std::endl; + if (debian_pkg_replaces && *debian_pkg_replaces) { + controlValues["Replaces"] = debian_pkg_replaces; } + const std::string strGenWDIR(this->GetOption("GEN_WDIR")); const std::string shlibsfilename = strGenWDIR + "/shlibs"; const char* debian_pkg_shlibs = @@ -371,314 +787,74 @@ int cmCPackDebGenerator::createDeb() "fi\n"; } - cmArchiveWrite::Compress tar_compression_type = cmArchiveWrite::CompressGZip; - const char* debian_compression_type = - this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"); - if (!debian_compression_type) { - debian_compression_type = "gzip"; - } - - std::string compression_suffix; - if (!strcmp(debian_compression_type, "lzma")) { - compression_suffix = ".lzma"; - tar_compression_type = cmArchiveWrite::CompressLZMA; - } else if (!strcmp(debian_compression_type, "xz")) { - compression_suffix = ".xz"; - tar_compression_type = cmArchiveWrite::CompressXZ; - } else if (!strcmp(debian_compression_type, "bzip2")) { - compression_suffix = ".bz2"; - tar_compression_type = cmArchiveWrite::CompressBZip2; - } else if (!strcmp(debian_compression_type, "gzip")) { - compression_suffix = ".gz"; - tar_compression_type = cmArchiveWrite::CompressGZip; - } else if (!strcmp(debian_compression_type, "none")) { - compression_suffix.clear(); - tar_compression_type = cmArchiveWrite::CompressNone; - } else { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error unrecognized compression type: " - << debian_compression_type << std::endl); - } - - const char* debian_archive_type = - this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"); - if (!debian_archive_type) { - debian_archive_type = "paxr"; + DebGenerator gen( + Logger, this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"), strGenWDIR, + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), + this->GetOption("CPACK_TEMPORARY_DIRECTORY"), + this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"), + this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, gen_shibs, + shlibsfilename, this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST"), postinst, + this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM"), postrm, + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"), + this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"), + packageFiles); + + if (!gen.generate()) { + return 0; } + return 1; +} - std::string filename_data_tar = - strGenWDIR + "/data.tar" + compression_suffix; - - // atomic file generation for data.tar - { - cmGeneratedFileStream fileStream_data_tar; - fileStream_data_tar.Open(filename_data_tar, false, true); - if (!fileStream_data_tar) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error opening the file \"" - << filename_data_tar << "\" for writing" << std::endl); - return 0; - } - cmArchiveWrite data_tar(fileStream_data_tar, tar_compression_type, - debian_archive_type); - - // uid/gid should be the one of the root user, and this root user has - // always uid/gid equal to 0. - data_tar.SetUIDAndGID(0u, 0u); - data_tar.SetUNAMEAndGNAME("root", "root"); - - // now add all directories which have to be compressed - // collect all top level install dirs for that - // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would - // give /usr and /opt - size_t topLevelLength = strGenWDIR.length(); - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "WDIR: \"" << strGenWDIR << "\", length = " << topLevelLength - << std::endl); - std::set<std::string> orderedFiles; - - // we have to reconstruct the parent folders as well - - for (std::string currentPath : packageFiles) { - while (currentPath != strGenWDIR) { - // the last one IS strGenWDIR, but we do not want this one: - // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application - // should not add XXX/application - orderedFiles.insert(currentPath); - currentPath = cmSystemTools::CollapseCombinedPath(currentPath, ".."); - } - } - - for (std::string const& file : orderedFiles) { - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "FILEIT: \"" << file << "\"" << std::endl); - std::string::size_type slashPos = file.find('/', topLevelLength + 1); - std::string relativeDir = - file.substr(topLevelLength, slashPos - topLevelLength); - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "RELATIVEDIR: \"" << relativeDir << "\"" << std::endl); - -#ifdef WIN32 - std::string mode_t_adt_filename = file + ":cmake_mode_t"; - cmsys::ifstream permissionStream(mode_t_adt_filename.c_str()); - - mode_t permissions = 0; - - if (permissionStream) { - permissionStream >> std::oct >> permissions; - } - - if (permissions != 0) { - data_tar.SetPermissions(permissions); - } else if (cmSystemTools::FileIsDirectory(file)) { - data_tar.SetPermissions(0755); - } else { - data_tar.ClearPermissions(); - } -#endif - - // do not recurse because the loop will do it - if (!data_tar.Add(file, topLevelLength, ".", false)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Problem adding file to tar:" - << std::endl - << "#top level directory: " << strGenWDIR << std::endl - << "#file: " << file << std::endl - << "#error:" << data_tar.GetError() << std::endl); - return 0; - } - } - } // scope for file generation +int cmCPackDebGenerator::createDbgsymDDeb() +{ + // Packages containing debug symbols follow the same structure as .debs + // but have different metadata and content. - std::string md5filename = strGenWDIR + "/md5sums"; - { - // the scope is needed for cmGeneratedFileStream - cmGeneratedFileStream out(md5filename); - - std::string topLevelWithTrailingSlash = - this->GetOption("CPACK_TEMPORARY_DIRECTORY"); - topLevelWithTrailingSlash += '/'; - for (std::string const& file : packageFiles) { - // hash only regular files - if (cmSystemTools::FileIsDirectory(file) || - cmSystemTools::FileIsSymlink(file)) { - continue; - } + std::map<std::string, std::string> controlValues; + // debian policy enforce lower case for package name + std::string packageNameLower = cmsys::SystemTools::LowerCase( + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME")); + const char* debian_pkg_version = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION"); - std::string output = - cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5); - if (output.empty()) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Problem computing the md5 of " << file << std::endl); - } + controlValues["Package"] = packageNameLower + "-dbgsym"; + controlValues["Package-Type"] = "ddeb"; + controlValues["Version"] = debian_pkg_version; + controlValues["Auto-Built-Package"] = "debug-symbols"; + controlValues["Depends"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME") + + std::string(" (= ") + debian_pkg_version + ")"; + controlValues["Section"] = "debug"; + controlValues["Priority"] = "optional"; + controlValues["Architecture"] = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE"); + controlValues["Maintainer"] = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER"); + controlValues["Description"] = + std::string("debug symbols for ") + packageNameLower; - output += " " + file + "\n"; - // debian md5sums entries are like this: - // 014f3604694729f3bf19263bac599765 usr/bin/ccmake - // thus strip the full path (with the trailing slash) - cmSystemTools::ReplaceString(output, topLevelWithTrailingSlash.c_str(), - ""); - out << output; - } - // each line contains a eol. - // Do not end the md5sum file with yet another (invalid) + const char* debian_pkg_source = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); + if (debian_pkg_source && *debian_pkg_source) { + controlValues["Source"] = debian_pkg_source; } - - std::string filename_control_tar = strGenWDIR + "/control.tar.gz"; - // atomic file generation for control.tar - { - cmGeneratedFileStream fileStream_control_tar; - fileStream_control_tar.Open(filename_control_tar, false, true); - if (!fileStream_control_tar) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error opening the file \"" << filename_control_tar - << "\" for writing" - << std::endl); - return 0; - } - cmArchiveWrite control_tar(fileStream_control_tar, - cmArchiveWrite::CompressGZip, - debian_archive_type); - - // sets permissions and uid/gid for the files - control_tar.SetUIDAndGID(0u, 0u); - control_tar.SetUNAMEAndGNAME("root", "root"); - - /* permissions are set according to - https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners - and - https://lintian.debian.org/tags/control-file-has-bad-permissions.html - */ - const mode_t permission644 = 0644; - const mode_t permissionExecute = 0111; - const mode_t permission755 = permission644 | permissionExecute; - - // for md5sum and control (that we have generated here), we use 644 - // (RW-R--R--) - // so that deb lintian doesn't warn about it - control_tar.SetPermissions(permission644); - - // adds control and md5sums - if (!control_tar.Add(md5filename, strGenWDIR.length(), ".") || - !control_tar.Add(strGenWDIR + "/control", strGenWDIR.length(), ".")) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error adding file to tar:" - << std::endl - << "#top level directory: " << strGenWDIR << std::endl - << "#file: \"control\" or \"md5sums\"" << std::endl - << "#error:" << control_tar.GetError() << std::endl); - return 0; - } - - // adds generated shlibs file - if (gen_shibs) { - if (!control_tar.Add(shlibsfilename, strGenWDIR.length(), ".")) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error adding file to tar:" - << std::endl - << "#top level directory: " << strGenWDIR << std::endl - << "#file: \"shlibs\"" << std::endl - << "#error:" << control_tar.GetError() << std::endl); - return 0; - } - } - - // adds LDCONFIG related files - if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) { - control_tar.SetPermissions(permission755); - if (!control_tar.Add(postinst, strGenWDIR.length(), ".")) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error adding file to tar:" - << std::endl - << "#top level directory: " << strGenWDIR << std::endl - << "#file: \"postinst\"" << std::endl - << "#error:" << control_tar.GetError() << std::endl); - return 0; - } - control_tar.SetPermissions(permission644); - } - - if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM")) { - control_tar.SetPermissions(permission755); - if (!control_tar.Add(postrm, strGenWDIR.length(), ".")) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error adding file to tar:" - << std::endl - << "#top level directory: " << strGenWDIR << std::endl - << "#file: \"postinst\"" << std::endl - << "#error:" << control_tar.GetError() << std::endl); - return 0; - } - control_tar.SetPermissions(permission644); - } - - // for the other files, we use - // -either the original permission on the files - // -either a permission strictly defined by the Debian policies - const char* controlExtra = - this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"); - if (controlExtra) { - // permissions are now controlled by the original file permissions - - const bool permissionStrictPolicy = - this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"); - - static const char* strictFiles[] = { "config", "postinst", "postrm", - "preinst", "prerm" }; - std::set<std::string> setStrictFiles( - strictFiles, - strictFiles + sizeof(strictFiles) / sizeof(strictFiles[0])); - - // default - control_tar.ClearPermissions(); - - std::vector<std::string> controlExtraList; - cmSystemTools::ExpandListArgument(controlExtra, controlExtraList); - for (std::string const& i : controlExtraList) { - std::string filenamename = cmsys::SystemTools::GetFilenameName(i); - std::string localcopy = strGenWDIR + "/" + filenamename; - - if (permissionStrictPolicy) { - control_tar.SetPermissions(setStrictFiles.count(filenamename) - ? permission755 - : permission644); - } - - // if we can copy the file, it means it does exist, let's add it: - if (cmsys::SystemTools::CopyFileIfDifferent(i, localcopy)) { - control_tar.Add(localcopy, strGenWDIR.length(), "."); - } - } - } + const char* debian_build_ids = this->GetOption("GEN_BUILD_IDS"); + if (debian_build_ids && *debian_build_ids) { + controlValues["Build-Ids"] = debian_build_ids; } - // ar -r your-package-name.deb debian-binary control.tar.* data.tar.* - // A debian package .deb is simply an 'ar' archive. The only subtle - // difference is that debian uses the BSD ar style archive whereas most - // Linux distro have a GNU ar. - // See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info - std::string const outputDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); - std::string const outputName = this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"); - std::string const outputPath = outputDir + "/" + outputName; - std::string const tlDir = strGenWDIR + "/"; - cmGeneratedFileStream debStream; - debStream.Open(outputPath, false, true); - cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd"); + DebGenerator gen( + Logger, this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"), + this->GetOption("GEN_DBGSYMDIR"), - // uid/gid should be the one of the root user, and this root user has - // always uid/gid equal to 0. - deb.SetUIDAndGID(0u, 0u); - deb.SetUNAMEAndGNAME("root", "root"); + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), + this->GetOption("CPACK_TEMPORARY_DIRECTORY"), + this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"), + this->GetOption("GEN_CPACK_DEBIAN_ARCHIVE_TYPE"), controlValues, false, "", + false, "", false, "", nullptr, + this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"), + packageFiles); - if (!deb.Add(tlDir + "debian-binary", tlDir.length()) || - !deb.Add(tlDir + "control.tar.gz", tlDir.length()) || - !deb.Add(tlDir + "data.tar" + compression_suffix, tlDir.length())) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error creating debian package:" - << std::endl - << "#top level directory: " << outputDir << std::endl - << "#file: " << outputName << std::endl - << "#error:" << deb.GetError() << std::endl); + if (!gen.generate()) { return 0; } return 1; diff --git a/Source/CPack/cmCPackDebGenerator.h b/Source/CPack/cmCPackDebGenerator.h index b4f0c79..2244fe7 100644 --- a/Source/CPack/cmCPackDebGenerator.h +++ b/Source/CPack/cmCPackDebGenerator.h @@ -65,6 +65,8 @@ protected: private: int createDeb(); + int createDbgsymDDeb(); + std::vector<std::string> packageFiles; }; diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index fccbc95..668a387 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -5,6 +5,7 @@ #include "cmCTest.h" #include "cmCTestTestHandler.h" #include "cmGlobalGenerator.h" +#include "cmMakefile.h" #include "cmSystemTools.h" #include "cmWorkingDirectory.h" #include "cmake.h" @@ -210,9 +211,14 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) if (this->BuildNoCMake) { // Make the generator available for the Build call below. - cm.SetGlobalGenerator(cm.CreateGlobalGenerator(this->BuildGenerator)); - cm.SetGeneratorPlatform(this->BuildGeneratorPlatform); - cm.SetGeneratorToolset(this->BuildGeneratorToolset); + cmGlobalGenerator* gen = cm.CreateGlobalGenerator(this->BuildGenerator); + cm.SetGlobalGenerator(gen); + if (!this->BuildGeneratorPlatform.empty()) { + cmMakefile mf(gen, cm.GetCurrentSnapshot()); + if (!gen->SetGeneratorPlatform(this->BuildGeneratorPlatform, &mf)) { + return 1; + } + } // Load the cache to make CMAKE_MAKE_PROGRAM available. cm.LoadCache(this->BinaryDir); diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 3aa59d6..22ae340 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -171,7 +171,7 @@ void CCONV cmAddLinkDirectoryForTarget(void* arg, const char* tgt, " for directory ", d); return; } - t->AddLinkDirectory(d); + t->InsertLinkDirectory(d, mf->GetBacktrace()); } void CCONV cmAddExecutable(void* arg, const char* exename, int numSrcs, diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 15fbd40..873372f 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -101,6 +101,7 @@ # include "cmRemoveDefinitionsCommand.h" # include "cmSourceGroupCommand.h" # include "cmSubdirDependsCommand.h" +# include "cmTargetLinkDirectoriesCommand.h" # include "cmTargetLinkOptionsCommand.h" # include "cmUseMangledMesaCommand.h" # include "cmUtilitySourceCommand.h" @@ -278,6 +279,8 @@ void GetProjectCommands(cmState* state) state->AddBuiltinCommand("link_libraries", new cmLinkLibrariesCommand); state->AddBuiltinCommand("target_link_options", new cmTargetLinkOptionsCommand); + state->AddBuiltinCommand("target_link_directories", + new cmTargetLinkDirectoriesCommand); state->AddBuiltinCommand("load_cache", new cmLoadCacheCommand); state->AddBuiltinCommand("qt_wrap_cpp", new cmQTWrapCPPCommand); state->AddBuiltinCommand("qt_wrap_ui", new cmQTWrapUICommand); diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index a3e135f..0e48ca8 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -357,10 +357,10 @@ cmComputeLinkInformation::cmComputeLinkInformation( } // Add the search path entries requested by the user to path ordering. - this->OrderLinkerSearchPath->AddUserDirectories( - this->Target->GetLinkDirectories()); - this->OrderRuntimeSearchPath->AddUserDirectories( - this->Target->GetLinkDirectories()); + std::vector<std::string> directories; + this->Target->GetLinkDirectories(directories, config, this->LinkLanguage); + this->OrderLinkerSearchPath->AddUserDirectories(directories); + this->OrderRuntimeSearchPath->AddUserDirectories(directories); // Set up the implicit link directories. this->LoadImplicitLinkInfo(); @@ -387,8 +387,7 @@ cmComputeLinkInformation::cmComputeLinkInformation( if (this->OldLinkDirMode) { // Construct a mask to not bother with this behavior for link // directories already specified by the user. - std::vector<std::string> const& dirs = this->Target->GetLinkDirectories(); - this->OldLinkDirMask.insert(dirs.begin(), dirs.end()); + this->OldLinkDirMask.insert(directories.begin(), directories.end()); } this->CMP0060Warn = this->Makefile->PolicyOptionalWarningEnabled( diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 7f42035..024e641 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -98,6 +98,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte, cmGeneratorExpression::BuildInterface, properties, missingTargets); + this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte, + cmGeneratorExpression::BuildInterface, + properties, missingTargets); this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte, cmGeneratorExpression::BuildInterface, properties, missingTargets); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index d6573b8..4cf9dd7 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -451,6 +451,37 @@ void cmExportFileGenerator::PopulateLinkDependsInterface( } } +void cmExportFileGenerator::PopulateLinkDirectoriesInterface( + cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, std::vector<std::string>& missingTargets) +{ + cmGeneratorTarget* gt = tei->Target; + assert(preprocessRule == cmGeneratorExpression::InstallInterface); + + const char* propName = "INTERFACE_LINK_DIRECTORIES"; + const char* input = gt->GetProperty(propName); + + if (!input) { + return; + } + + if (!*input) { + properties[propName].clear(); + return; + } + + std::string prepro = + cmGeneratorExpression::Preprocess(input, preprocessRule, true); + if (!prepro.empty()) { + this->ResolveTargetsInGeneratorExpressions(prepro, gt, missingTargets); + + if (!checkInterfaceDirs(prepro, gt, propName)) { + return; + } + properties[propName] = prepro; + } +} + void cmExportFileGenerator::PopulateInterfaceProperty( const std::string& propName, cmGeneratorTarget* target, cmGeneratorExpression::PreprocessContext preprocessRule, diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index 6ca2e07..41c6538 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -147,6 +147,10 @@ protected: cmTargetExport* target, cmGeneratorExpression::PreprocessContext preprocessRule, ImportPropertyMap& properties, std::vector<std::string>& missingTargets); + void PopulateLinkDirectoriesInterface( + cmTargetExport* target, + cmGeneratorExpression::PreprocessContext preprocessRule, + ImportPropertyMap& properties, std::vector<std::string>& missingTargets); void PopulateLinkDependsInterface( cmTargetExport* target, cmGeneratorExpression::PreprocessContext preprocessRule, diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index bfb7a05..e444087 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -106,6 +106,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt, cmGeneratorExpression::InstallInterface, properties, missingTargets); + this->PopulateLinkDirectoriesInterface( + te, cmGeneratorExpression::InstallInterface, properties, missingTargets); this->PopulateLinkDependsInterface( te, cmGeneratorExpression::InstallInterface, properties, missingTargets); diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 658e9a7..2727d9a 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -168,7 +168,7 @@ static std::string stripAllGeneratorExpressions(const std::string& input) const char* c = input.c_str() + pos; const char* const cStart = c; for (; *c; ++c) { - if (c[0] == '$' && c[1] == '<') { + if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) { ++nestingLevel; ++c; continue; @@ -243,7 +243,7 @@ static std::string stripExportInterface( const char* c = input.c_str() + pos; const char* const cStart = c; for (; *c; ++c) { - if (c[0] == '$' && c[1] == '<') { + if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) { ++nestingLevel; ++c; continue; @@ -310,7 +310,7 @@ void cmGeneratorExpression::Split(const std::string& input, const char* c = input.c_str() + pos; const char* const cStart = c; for (; *c; ++c) { - if (c[0] == '$' && c[1] == '<') { + if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) { ++nestingLevel; ++c; continue; diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index 8176d5c..9c05f60 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -63,6 +63,15 @@ public: static std::string StripEmptyListElements(const std::string& input); + static inline bool StartsWithGeneratorExpression(const std::string& input) + { + return input.length() >= 2 && input[0] == '$' && input[1] == '<'; + } + static inline bool StartsWithGeneratorExpression(const char* input) + { + return input != nullptr && input[0] == '$' && input[1] == '<'; + } + private: cmListFileBacktrace Backtrace; }; diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index 8b1697b..a5134c3 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -28,6 +28,7 @@ class cmGeneratorTarget; SELECT(F, EvaluatingSources, SOURCES) \ SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES) \ SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS) \ + SELECT(F, EvaluatingLinkDirectories, LINK_DIRECTORIES) \ SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS) #define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \ diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index a58d3cb..29c6058 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -103,6 +103,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) , DebugCompileFeaturesDone(false) , DebugCompileDefinitionsDone(false) , DebugLinkOptionsDone(false) + , DebugLinkDirectoriesDone(false) , DebugSourcesDone(false) , LinkImplementationLanguageIsContextDependent(true) , UtilityItemsDone(false) @@ -133,6 +134,10 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) t->GetLinkOptionsBacktraces(), this->LinkOptionsEntries); + CreatePropertyGeneratorExpressions(t->GetLinkDirectoriesEntries(), + t->GetLinkDirectoriesBacktraces(), + this->LinkDirectoriesEntries); + CreatePropertyGeneratorExpressions(t->GetSourceEntries(), t->GetSourceBacktraces(), this->SourceEntries, true); @@ -150,6 +155,7 @@ cmGeneratorTarget::~cmGeneratorTarget() cmDeleteAll(this->CompileFeaturesEntries); cmDeleteAll(this->CompileDefinitionsEntries); cmDeleteAll(this->LinkOptionsEntries); + cmDeleteAll(this->LinkDirectoriesEntries); cmDeleteAll(this->SourceEntries); cmDeleteAll(this->LinkInformation); } @@ -1704,11 +1710,6 @@ cmListFileBacktrace cmGeneratorTarget::GetBacktrace() const return this->Target->GetBacktrace(); } -const std::vector<std::string>& cmGeneratorTarget::GetLinkDirectories() const -{ - return this->Target->GetLinkDirectories(); -} - const std::set<std::string>& cmGeneratorTarget::GetUtilities() const { return this->Target->GetUtilities(); @@ -3068,6 +3069,126 @@ void cmGeneratorTarget::GetStaticLibraryLinkOptions( } namespace { +void processLinkDirectories( + cmGeneratorTarget const* tgt, + const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, + std::vector<std::string>& directories, + std::unordered_set<std::string>& uniqueDirectories, + cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config, + bool debugDirectories, std::string const& language) +{ + for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { + cmLinkImplItem const& item = entry->LinkImplItem; + std::string const& targetName = item.AsStr(); + + std::vector<std::string> entryDirectories; + cmSystemTools::ExpandListArgument( + entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, + dagChecker, language), + entryDirectories); + + std::string usedDirectories; + for (std::string& entryDirectory : entryDirectories) { + if (!cmSystemTools::FileIsFullPath(entryDirectory)) { + std::ostringstream e; + bool noMessage = false; + cmake::MessageType messageType = cmake::FATAL_ERROR; + if (!targetName.empty()) { + /* clang-format off */ + e << "Target \"" << targetName << "\" contains relative " + "path in its INTERFACE_LINK_DIRECTORIES:\n" + " \"" << entryDirectory << "\""; + /* clang-format on */ + } else { + switch (tgt->GetPolicyStatusCMP0081()) { + case cmPolicies::WARN: { + e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0081) << "\n"; + messageType = cmake::AUTHOR_WARNING; + } break; + case cmPolicies::OLD: + noMessage = true; + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Issue the fatal message. + break; + } + e << "Found relative path while evaluating link directories of " + "\"" + << tgt->GetName() << "\":\n \"" << entryDirectory << "\"\n"; + } + if (!noMessage) { + tgt->GetLocalGenerator()->IssueMessage(messageType, e.str()); + if (messageType == cmake::FATAL_ERROR) { + return; + } + } + } + + // Sanitize the path the same way the link_directories command does + // in case projects set the LINK_DIRECTORIES property directly. + cmSystemTools::ConvertToUnixSlashes(entryDirectory); + if (uniqueDirectories.insert(entryDirectory).second) { + directories.push_back(entryDirectory); + if (debugDirectories) { + usedDirectories += " * " + entryDirectory + "\n"; + } + } + } + if (!usedDirectories.empty()) { + tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage( + cmake::LOG, + std::string("Used link directories for target ") + tgt->GetName() + + ":\n" + usedDirectories, + entry->ge->GetBacktrace()); + } + } +} +} + +void cmGeneratorTarget::GetLinkDirectories(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const +{ + std::unordered_set<std::string> uniqueDirectories; + + cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_DIRECTORIES", nullptr, + nullptr); + + std::vector<std::string> debugProperties; + const char* debugProp = + this->Makefile->GetDefinition("CMAKE_DEBUG_TARGET_PROPERTIES"); + if (debugProp) { + cmSystemTools::ExpandListArgument(debugProp, debugProperties); + } + + bool debugDirectories = !this->DebugLinkDirectoriesDone && + std::find(debugProperties.begin(), debugProperties.end(), + "LINK_DIRECTORIES") != debugProperties.end(); + + if (this->GlobalGenerator->GetConfigureDoneCMP0026()) { + this->DebugLinkDirectoriesDone = true; + } + + processLinkDirectories(this, this->LinkDirectoriesEntries, result, + uniqueDirectories, &dagChecker, config, + debugDirectories, language); + + std::vector<cmGeneratorTarget::TargetPropertyEntry*> + linkInterfaceLinkDirectoriesEntries; + + AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", + linkInterfaceLinkDirectoriesEntries); + + processLinkDirectories(this, linkInterfaceLinkDirectoriesEntries, result, + uniqueDirectories, &dagChecker, config, + debugDirectories, language); + + cmDeleteAll(linkInterfaceLinkDirectoriesEntries); +} + +namespace { void processLinkDepends( cmGeneratorTarget const* tgt, const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries, diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 9d8c9f5..bfd95ac 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -273,8 +273,6 @@ public: cmListFileBacktrace GetBacktrace() const; - const std::vector<std::string>& GetLinkDirectories() const; - std::set<std::string> const& GetUtilities() const; cmListFileBacktrace const* GetUtilityBacktrace(const std::string& u) const; @@ -435,6 +433,10 @@ public: const std::string& config, const std::string& language) const; + void GetLinkDirectories(std::vector<std::string>& result, + const std::string& config, + const std::string& language) const; + void GetLinkDepends(std::vector<std::string>& result, const std::string& config, const std::string& language) const; @@ -825,6 +827,7 @@ private: std::vector<TargetPropertyEntry*> CompileFeaturesEntries; std::vector<TargetPropertyEntry*> CompileDefinitionsEntries; std::vector<TargetPropertyEntry*> LinkOptionsEntries; + std::vector<TargetPropertyEntry*> LinkDirectoriesEntries; std::vector<TargetPropertyEntry*> SourceEntries; mutable std::set<std::string> LinkImplicitNullProperties; @@ -874,6 +877,7 @@ private: mutable bool DebugCompileFeaturesDone; mutable bool DebugCompileDefinitionsDone; mutable bool DebugLinkOptionsDone; + mutable bool DebugLinkDirectoriesDone; mutable bool DebugSourcesDone; mutable bool LinkImplementationLanguageIsContextDependent; mutable bool UtilityItemsDone; diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 5ea323a..82fcaad 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -938,6 +938,7 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( configArg += "Debug"; } makeCommand.push_back(configArg); + makeCommand.push_back("/p:Platform=" + this->GetPlatformName()); makeCommand.push_back(std::string("/p:VisualStudioVersion=") + this->GetIDEVersion()); diff --git a/Source/cmIncludeDirectoryCommand.cxx b/Source/cmIncludeDirectoryCommand.cxx index eaaf64d..caec67d 100644 --- a/Source/cmIncludeDirectoryCommand.cxx +++ b/Source/cmIncludeDirectoryCommand.cxx @@ -5,6 +5,7 @@ #include <algorithm> #include <set> +#include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmSystemTools.h" @@ -69,11 +70,6 @@ bool cmIncludeDirectoryCommand::InitialPass( return true; } -static bool StartsWithGeneratorExpression(const std::string& input) -{ - return input[0] == '$' && input[1] == '<'; -} - // do a lot of cleanup on the arguments because this is one place where folks // sometimes take the output of a program and pass it directly into this // command not thinking that a single argument could be filled with spaces @@ -124,7 +120,7 @@ void cmIncludeDirectoryCommand::NormalizeInclude(std::string& inc) cmSystemTools::ConvertToUnixSlashes(inc); if (!cmSystemTools::FileIsFullPath(inc)) { - if (!StartsWithGeneratorExpression(inc)) { + if (!cmGeneratorExpression::StartsWithGeneratorExpression(inc)) { std::string tmp = this->Makefile->GetCurrentSourceDirectory(); tmp += "/"; tmp += inc; diff --git a/Source/cmJsonObjectDictionary.h b/Source/cmJsonObjectDictionary.h new file mode 100644 index 0000000..a4d41f3 --- /dev/null +++ b/Source/cmJsonObjectDictionary.h @@ -0,0 +1,46 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> + +// Vocabulary: + +static const std::string kARTIFACTS_KEY = "artifacts"; +static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; +static const std::string kCOMPILE_FLAGS_KEY = "compileFlags"; +static const std::string kCONFIGURATIONS_KEY = "configurations"; +static const std::string kDEFINES_KEY = "defines"; +static const std::string kFILE_GROUPS_KEY = "fileGroups"; +static const std::string kFRAMEWORK_PATH_KEY = "frameworkPath"; +static const std::string kFULL_NAME_KEY = "fullName"; +static const std::string kINCLUDE_PATH_KEY = "includePath"; +static const std::string kIS_CMAKE_KEY = "isCMake"; +static const std::string kIS_GENERATED_KEY = "isGenerated"; +static const std::string kIS_SYSTEM_KEY = "isSystem"; +static const std::string kIS_TEMPORARY_KEY = "isTemporary"; +static const std::string kKEY_KEY = "key"; +static const std::string kLANGUAGE_KEY = "language"; +static const std::string kLINKER_LANGUAGE_KEY = "linkerLanguage"; +static const std::string kLINK_FLAGS_KEY = "linkFlags"; +static const std::string kLINK_LANGUAGE_FLAGS_KEY = "linkLanguageFlags"; +static const std::string kLINK_LIBRARIES_KEY = "linkLibraries"; +static const std::string kLINK_PATH_KEY = "linkPath"; +static const std::string kNAME_KEY = "name"; +static const std::string kPATH_KEY = "path"; +static const std::string kPROJECTS_KEY = "projects"; +static const std::string kPROPERTIES_KEY = "properties"; +static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; +static const std::string kSOURCES_KEY = "sources"; +static const std::string kSYSROOT_KEY = "sysroot"; +static const std::string kTARGETS_KEY = "targets"; +static const std::string kTYPE_KEY = "type"; +static const std::string kVALUE_KEY = "value"; +static const std::string kHAS_INSTALL_RULE = "hasInstallRule"; +static const std::string kINSTALL_PATHS = "installPaths"; +static const std::string kCTEST_NAME = "ctestName"; +static const std::string kCTEST_COMMAND = "ctestCommand"; +static const std::string kCTEST_INFO = "ctestInfo"; +static const std::string kMINIMUM_CMAKE_VERSION = "minimumCMakeVersion"; +static const std::string kIS_GENERATOR_PROVIDED_KEY = "isGeneratorProvided"; +static const std::string kIS_INTERFACE_SOURCES_KEY = "isInterfaceSources"; diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx new file mode 100644 index 0000000..a7db75f --- /dev/null +++ b/Source/cmJsonObjects.cxx @@ -0,0 +1,823 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmJsonObjects.h" // IWYU pragma: keep + +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmInstallGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmJsonObjectDictionary.h" +#include "cmJsonObjects.h" +#include "cmLinkLineComputer.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmProperty.h" +#include "cmSourceFile.h" +#include "cmState.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTest.h" +#include "cmake.h" + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <functional> +#include <limits> +#include <map> +#include <set> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace { + +std::vector<std::string> getConfigurations(const cmake* cm) +{ + std::vector<std::string> configurations; + auto makefiles = cm->GetGlobalGenerator()->GetMakefiles(); + if (makefiles.empty()) { + return configurations; + } + + makefiles[0]->GetConfigurations(configurations); + if (configurations.empty()) { + configurations.push_back(""); + } + return configurations; +} + +bool hasString(const Json::Value& v, const std::string& s) +{ + return !v.isNull() && + std::any_of(v.begin(), v.end(), + [s](const Json::Value& i) { return i.asString() == s; }); +} + +template <class T> +Json::Value fromStringList(const T& in) +{ + Json::Value result = Json::arrayValue; + for (std::string const& i : in) { + result.append(i); + } + return result; +} + +} // namespace + +void cmGetCMakeInputs(const cmGlobalGenerator* gg, + const std::string& sourceDir, + const std::string& buildDir, + std::vector<std::string>* internalFiles, + std::vector<std::string>* explicitFiles, + std::vector<std::string>* tmpFiles) +{ + const std::string cmakeRootDir = cmSystemTools::GetCMakeRoot() + '/'; + std::vector<cmMakefile*> const& makefiles = gg->GetMakefiles(); + for (cmMakefile const* mf : makefiles) { + for (std::string const& lf : mf->GetListFiles()) { + + const std::string startOfFile = lf.substr(0, cmakeRootDir.size()); + const bool isInternal = (startOfFile == cmakeRootDir); + const bool isTemporary = !isInternal && (lf.find(buildDir + '/') == 0); + + std::string toAdd = lf; + if (!sourceDir.empty()) { + const std::string& relative = + cmSystemTools::RelativePath(sourceDir, lf); + if (toAdd.size() > relative.size()) { + toAdd = relative; + } + } + + if (isInternal) { + if (internalFiles) { + internalFiles->push_back(std::move(toAdd)); + } + } else { + if (isTemporary) { + if (tmpFiles) { + tmpFiles->push_back(std::move(toAdd)); + } + } else { + if (explicitFiles) { + explicitFiles->push_back(std::move(toAdd)); + } + } + } + } + } +} + +Json::Value cmDumpCMakeInputs(const cmake* cm) +{ + const cmGlobalGenerator* gg = cm->GetGlobalGenerator(); + const std::string& buildDir = cm->GetHomeOutputDirectory(); + const std::string& sourceDir = cm->GetHomeDirectory(); + + std::vector<std::string> internalFiles; + std::vector<std::string> explicitFiles; + std::vector<std::string> tmpFiles; + cmGetCMakeInputs(gg, sourceDir, buildDir, &internalFiles, &explicitFiles, + &tmpFiles); + + Json::Value array = Json::arrayValue; + + Json::Value tmp = Json::objectValue; + tmp[kIS_CMAKE_KEY] = true; + tmp[kIS_TEMPORARY_KEY] = false; + tmp[kSOURCES_KEY] = fromStringList(internalFiles); + array.append(tmp); + + tmp = Json::objectValue; + tmp[kIS_CMAKE_KEY] = false; + tmp[kIS_TEMPORARY_KEY] = false; + tmp[kSOURCES_KEY] = fromStringList(explicitFiles); + array.append(tmp); + + tmp = Json::objectValue; + tmp[kIS_CMAKE_KEY] = false; + tmp[kIS_TEMPORARY_KEY] = true; + tmp[kSOURCES_KEY] = fromStringList(tmpFiles); + array.append(tmp); + + return array; +} + +const std::string kInterfaceSourcesLanguageDataKey = + "INTERFACE_SOURCES_LD_KEY"; +class LanguageData +{ +public: + bool operator==(const LanguageData& other) const; + + void SetDefines(const std::set<std::string>& defines); + + bool IsGenerated = false; + std::string Language; + std::string Flags; + std::vector<std::string> Defines; + std::vector<std::pair<std::string, bool>> IncludePathList; +}; + +bool LanguageData::operator==(const LanguageData& other) const +{ + return Language == other.Language && Defines == other.Defines && + Flags == other.Flags && IncludePathList == other.IncludePathList && + IsGenerated == other.IsGenerated; +} + +void LanguageData::SetDefines(const std::set<std::string>& defines) +{ + std::vector<std::string> result; + result.reserve(defines.size()); + for (std::string const& i : defines) { + result.push_back(i); + } + std::sort(result.begin(), result.end()); + Defines = std::move(result); +} + +struct FileGroupSources +{ + bool IsInterfaceSources; + std::vector<std::string> Files; +}; + +namespace std { + +template <> +struct hash<LanguageData> +{ + std::size_t operator()(const LanguageData& in) const + { + using std::hash; + size_t result = + hash<std::string>()(in.Language) ^ hash<std::string>()(in.Flags); + for (auto const& i : in.IncludePathList) { + result = result ^ + (hash<std::string>()(i.first) ^ + (i.second ? std::numeric_limits<size_t>::max() : 0)); + } + for (auto const& i : in.Defines) { + result = result ^ hash<std::string>()(i); + } + result = + result ^ (in.IsGenerated ? std::numeric_limits<size_t>::max() : 0); + return result; + } +}; + +} // namespace std + +static Json::Value DumpSourceFileGroup(const LanguageData& data, + bool isInterfaceSource, + const std::vector<std::string>& files, + const std::string& baseDir) +{ + Json::Value result = Json::objectValue; + + if (isInterfaceSource) { + result[kIS_INTERFACE_SOURCES_KEY] = true; + } + if (!data.Language.empty()) { + result[kLANGUAGE_KEY] = data.Language; + } + if (!data.Flags.empty()) { + result[kCOMPILE_FLAGS_KEY] = data.Flags; + } + if (!data.IncludePathList.empty()) { + Json::Value includes = Json::arrayValue; + for (auto const& i : data.IncludePathList) { + Json::Value tmp = Json::objectValue; + tmp[kPATH_KEY] = i.first; + if (i.second) { + tmp[kIS_SYSTEM_KEY] = i.second; + } + includes.append(tmp); + } + result[kINCLUDE_PATH_KEY] = includes; + } + if (!data.Defines.empty()) { + result[kDEFINES_KEY] = fromStringList(data.Defines); + } + + result[kIS_GENERATED_KEY] = data.IsGenerated; + + Json::Value sourcesValue = Json::arrayValue; + for (auto const& i : files) { + const std::string relPath = cmSystemTools::RelativePath(baseDir, i); + sourcesValue.append(relPath.size() < i.size() ? relPath : i); + } + + result[kSOURCES_KEY] = sourcesValue; + return result; +} + +static void PopulateFileGroupData( + cmGeneratorTarget* target, bool isInterfaceSources, + const std::vector<cmSourceFile*>& files, const std::string& config, + const std::map<std::string, LanguageData>& languageDataMap, + std::unordered_map<LanguageData, FileGroupSources>& fileGroups) +{ + for (cmSourceFile* file : files) { + LanguageData fileData; + fileData.Language = file->GetLanguage(); + if (!fileData.Language.empty() || isInterfaceSources) { + const LanguageData& ld = isInterfaceSources + ? languageDataMap.at(kInterfaceSourcesLanguageDataKey) + : languageDataMap.at(fileData.Language); + cmLocalGenerator* lg = target->GetLocalGenerator(); + cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target, + fileData.Language); + + std::string compileFlags = ld.Flags; + const std::string COMPILE_FLAGS("COMPILE_FLAGS"); + if (const char* cflags = file->GetProperty(COMPILE_FLAGS)) { + lg->AppendFlags(compileFlags, + genexInterpreter.Evaluate(cflags, COMPILE_FLAGS)); + } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = file->GetProperty(COMPILE_OPTIONS)) { + lg->AppendCompileOptions( + compileFlags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); + } + fileData.Flags = compileFlags; + + // Add include directories from source file properties. + std::vector<std::string> includes; + + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincludes = file->GetProperty(INCLUDE_DIRECTORIES)) { + const std::string& evaluatedIncludes = + genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES); + lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file); + + for (const auto& include : includes) { + // INTERFACE_LIBRARY targets do not support the + // IsSystemIncludeDirectory call so just set it to false. + const bool isSystemInclude = isInterfaceSources + ? false + : target->IsSystemIncludeDirectory(include, config, + fileData.Language); + fileData.IncludePathList.push_back( + std::make_pair(include, isSystemInclude)); + } + } + + fileData.IncludePathList.insert(fileData.IncludePathList.end(), + ld.IncludePathList.begin(), + ld.IncludePathList.end()); + + const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); + std::set<std::string> defines; + if (const char* defs = file->GetProperty(COMPILE_DEFINITIONS)) { + lg->AppendDefines( + defines, genexInterpreter.Evaluate(defs, COMPILE_DEFINITIONS)); + } + + const std::string defPropName = + "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); + if (const char* config_defs = file->GetProperty(defPropName)) { + lg->AppendDefines( + defines, + genexInterpreter.Evaluate(config_defs, COMPILE_DEFINITIONS)); + } + + defines.insert(ld.Defines.begin(), ld.Defines.end()); + + fileData.SetDefines(defines); + } + + fileData.IsGenerated = file->GetPropertyAsBool("GENERATED"); + FileGroupSources& groupFileList = fileGroups[fileData]; + groupFileList.IsInterfaceSources = isInterfaceSources; + groupFileList.Files.push_back(file->GetFullPath()); + } +} + +static Json::Value DumpSourceFilesList( + cmGeneratorTarget* target, const std::string& config, + const std::map<std::string, LanguageData>& languageDataMap) +{ + const cmStateEnums::TargetType type = target->GetType(); + std::unordered_map<LanguageData, FileGroupSources> fileGroups; + + // Collect sourcefile groups: + + std::vector<cmSourceFile*> files; + if (type == cmStateEnums::INTERFACE_LIBRARY) { + // INTERFACE_LIBRARY targets do not create all the data structures + // associated with regular targets. If properties are explicitly specified + // for files in INTERFACE_SOURCES then we can get them through the Makefile + // rather than the target. + files = target->Makefile->GetSourceFiles(); + } else { + target->GetSourceFiles(files, config); + PopulateFileGroupData(target, false /* isInterfaceSources */, files, + config, languageDataMap, fileGroups); + } + + // Collect interface sourcefile groups: + + auto targetProp = target->Target->GetProperty("INTERFACE_SOURCES"); + if (targetProp != nullptr) { + cmGeneratorExpressionInterpreter genexInterpreter( + target->GetLocalGenerator(), config, target); + + auto evaluatedSources = cmsys::SystemTools::SplitString( + genexInterpreter.Evaluate(targetProp, "INTERFACE_SOURCES"), ';'); + + std::map<std::string, cmSourceFile*> filesMap; + for (auto file : files) { + filesMap[file->GetFullPath()] = file; + } + + std::vector<cmSourceFile*> interfaceSourceFiles; + for (const std::string& interfaceSourceFilePath : evaluatedSources) { + auto entry = filesMap.find(interfaceSourceFilePath); + if (entry != filesMap.end()) { + // use what we have since it has all the associated properties + interfaceSourceFiles.push_back(entry->second); + } else { + interfaceSourceFiles.push_back( + new cmSourceFile(target->Makefile, interfaceSourceFilePath)); + } + } + + PopulateFileGroupData(target, true /* isInterfaceSources */, + interfaceSourceFiles, config, languageDataMap, + fileGroups); + } + + const std::string& baseDir = target->Makefile->GetCurrentSourceDirectory(); + Json::Value result = Json::arrayValue; + for (auto const& it : fileGroups) { + Json::Value group = DumpSourceFileGroup( + it.first, it.second.IsInterfaceSources, it.second.Files, baseDir); + if (!group.isNull()) { + result.append(group); + } + } + + return result; +} + +static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, + const std::string& config) +{ + Json::Value result = Json::objectValue; + result[kCTEST_NAME] = testInfo->GetName(); + + // Concat command entries together. After the first should be the arguments + // for the command + std::string command; + for (auto const& cmd : testInfo->GetCommand()) { + command.append(cmd); + command.append(" "); + } + + // Remove any config specific variables from the output. + cmGeneratorExpression ge; + auto cge = ge.Parse(command); + const std::string& processed = cge->Evaluate(lg, config); + result[kCTEST_COMMAND] = processed; + + // Build up the list of properties that may have been specified + Json::Value properties = Json::arrayValue; + for (auto& prop : testInfo->GetProperties()) { + 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()); + const std::string& processed_value = cge_value->Evaluate(lg, config); + entry[kVALUE_KEY] = processed_value; + properties.append(entry); + } + result[kPROPERTIES_KEY] = properties; + + return result; +} + +static void DumpMakefileTests(cmLocalGenerator* lg, const std::string& config, + Json::Value* result) +{ + auto mf = lg->GetMakefile(); + std::vector<cmTest*> tests; + mf->GetTests(config, tests); + for (auto test : tests) { + Json::Value tmp = DumpCTestInfo(lg, test, config); + if (!tmp.isNull()) { + result->append(tmp); + } + } +} + +static Json::Value DumpCTestProjectList(const cmake* cm, + std::string const& config) +{ + Json::Value result = Json::arrayValue; + + auto globalGen = cm->GetGlobalGenerator(); + + for (const auto& projectIt : globalGen->GetProjectMap()) { + Json::Value pObj = Json::objectValue; + pObj[kNAME_KEY] = projectIt.first; + + Json::Value tests = Json::arrayValue; + + // Gather tests for every generator + for (const auto& lg : projectIt.second) { + // Make sure they're generated. + lg->GenerateTestFiles(); + DumpMakefileTests(lg, config, &tests); + } + + pObj[kCTEST_INFO] = tests; + + result.append(pObj); + } + + return result; +} + +static Json::Value DumpCTestConfiguration(const cmake* cm, + const std::string& config) +{ + Json::Value result = Json::objectValue; + result[kNAME_KEY] = config; + + result[kPROJECTS_KEY] = DumpCTestProjectList(cm, config); + + return result; +} + +static Json::Value DumpCTestConfigurationsList(const cmake* cm) +{ + Json::Value result = Json::arrayValue; + + for (const std::string& c : getConfigurations(cm)) { + result.append(DumpCTestConfiguration(cm, c)); + } + + return result; +} + +Json::Value cmDumpCTestInfo(const cmake* cm) +{ + Json::Value result = Json::objectValue; + result[kCONFIGURATIONS_KEY] = DumpCTestConfigurationsList(cm); + return result; +} + +static void GetTargetProperty( + cmGeneratorExpressionInterpreter& genexInterpreter, + cmGeneratorTarget* target, const char* propertyName, + std::vector<std::string>& propertyValue) +{ + auto targetProp = target->Target->GetProperty(propertyName); + if (targetProp != nullptr) { + propertyValue = cmsys::SystemTools::SplitString( + genexInterpreter.Evaluate(targetProp, propertyName), ';'); + } +} + +static void CreateInterfaceSourcesEntry( + cmLocalGenerator* lg, cmGeneratorTarget* target, const std::string& config, + std::map<std::string, LanguageData>& languageDataMap) +{ + LanguageData& ld = languageDataMap[kInterfaceSourcesLanguageDataKey]; + ld.Language = ""; + + cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target); + std::vector<std::string> propertyValue; + GetTargetProperty(genexInterpreter, target, "INTERFACE_INCLUDE_DIRECTORIES", + propertyValue); + for (std::string const& i : propertyValue) { + ld.IncludePathList.push_back( + std::make_pair(i, false /* isSystemInclude */)); + } + + propertyValue.clear(); + GetTargetProperty(genexInterpreter, target, + "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", propertyValue); + for (std::string const& i : propertyValue) { + ld.IncludePathList.push_back( + std::make_pair(i, true /* isSystemInclude */)); + } + + propertyValue.clear(); + GetTargetProperty(genexInterpreter, target, "INTERFACE_COMPILE_OPTIONS", + propertyValue); + for (const auto& s : propertyValue) { + ld.Flags += " " + s; + } + + propertyValue.clear(); + GetTargetProperty(genexInterpreter, target, "INTERFACE_COMPILE_DEFINITIONS", + propertyValue); + if (!propertyValue.empty()) { + std::set<std::string> defines(propertyValue.begin(), propertyValue.end()); + ld.SetDefines(defines); + } +} + +static Json::Value DumpTarget(cmGeneratorTarget* target, + const std::string& config) +{ + cmLocalGenerator* lg = target->GetLocalGenerator(); + const cmState* state = lg->GetState(); + + const cmStateEnums::TargetType type = target->GetType(); + const std::string typeName = state->GetTargetTypeName(type); + + Json::Value ttl = Json::arrayValue; + ttl.append("EXECUTABLE"); + ttl.append("STATIC_LIBRARY"); + ttl.append("SHARED_LIBRARY"); + ttl.append("MODULE_LIBRARY"); + ttl.append("OBJECT_LIBRARY"); + ttl.append("UTILITY"); + ttl.append("INTERFACE_LIBRARY"); + + if (!hasString(ttl, typeName) || target->IsImported()) { + return Json::Value(); + } + + Json::Value result = Json::objectValue; + result[kNAME_KEY] = target->GetName(); + result[kIS_GENERATOR_PROVIDED_KEY] = + target->Target->GetIsGeneratorProvided(); + result[kTYPE_KEY] = typeName; + result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory(); + result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory(); + result[kFULL_NAME_KEY] = target->GetFullName(config); + + if (target->Target->GetHaveInstallRule()) { + result[kHAS_INSTALL_RULE] = true; + + Json::Value installPaths = Json::arrayValue; + auto targetGenerators = target->Makefile->GetInstallGenerators(); + for (auto installGenerator : targetGenerators) { + auto installTargetGenerator = + dynamic_cast<cmInstallTargetGenerator*>(installGenerator); + if (installTargetGenerator != nullptr && + installTargetGenerator->GetTarget()->Target == target->Target) { + auto dest = installTargetGenerator->GetDestination(config); + + std::string installPath; + if (!dest.empty() && cmSystemTools::FileIsFullPath(dest)) { + installPath = dest; + } else { + std::string installPrefix = + target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); + installPath = installPrefix + '/' + dest; + } + + installPaths.append(installPath); + } + } + + result[kINSTALL_PATHS] = installPaths; + } + + if (target->HaveWellDefinedOutputFiles()) { + Json::Value artifacts = Json::arrayValue; + artifacts.append( + target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact)); + if (target->IsDLLPlatform()) { + artifacts.append( + target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact)); + const cmGeneratorTarget::OutputInfo* output = + target->GetOutputInfo(config); + if (output && !output->PdbDir.empty()) { + artifacts.append(output->PdbDir + '/' + target->GetPDBName(config)); + } + } + result[kARTIFACTS_KEY] = artifacts; + + result[kLINKER_LANGUAGE_KEY] = target->GetLinkerLanguage(config); + + std::string linkLibs; + std::string linkFlags; + std::string linkLanguageFlags; + std::string frameworkPath; + std::string linkPath; + cmLinkLineComputer linkLineComputer(lg, + lg->GetStateSnapshot().GetDirectory()); + lg->GetTargetFlags(&linkLineComputer, config, linkLibs, linkLanguageFlags, + linkFlags, frameworkPath, linkPath, target); + + linkLibs = cmSystemTools::TrimWhitespace(linkLibs); + linkFlags = cmSystemTools::TrimWhitespace(linkFlags); + linkLanguageFlags = cmSystemTools::TrimWhitespace(linkLanguageFlags); + frameworkPath = cmSystemTools::TrimWhitespace(frameworkPath); + linkPath = cmSystemTools::TrimWhitespace(linkPath); + + if (!cmSystemTools::TrimWhitespace(linkLibs).empty()) { + result[kLINK_LIBRARIES_KEY] = linkLibs; + } + if (!cmSystemTools::TrimWhitespace(linkFlags).empty()) { + result[kLINK_FLAGS_KEY] = linkFlags; + } + if (!cmSystemTools::TrimWhitespace(linkLanguageFlags).empty()) { + result[kLINK_LANGUAGE_FLAGS_KEY] = linkLanguageFlags; + } + if (!frameworkPath.empty()) { + result[kFRAMEWORK_PATH_KEY] = frameworkPath; + } + if (!linkPath.empty()) { + result[kLINK_PATH_KEY] = linkPath; + } + const std::string sysroot = + lg->GetMakefile()->GetSafeDefinition("CMAKE_SYSROOT"); + if (!sysroot.empty()) { + result[kSYSROOT_KEY] = sysroot; + } + } + + std::set<std::string> languages; + std::map<std::string, LanguageData> languageDataMap; + if (type == cmStateEnums::INTERFACE_LIBRARY) { + // INTERFACE_LIBRARY targets do not create all the data structures + // associated with regular targets. If properties are explicitly specified + // for files in INTERFACE_SOURCES then we can get them through the Makefile + // rather than the target. + for (auto file : target->Makefile->GetSourceFiles()) { + const std::string& language = file->GetLanguage(); + if (!language.empty()) { + languages.insert(language); + } + } + } else { + target->GetLanguages(languages, config); + } + + for (std::string const& lang : languages) { + LanguageData& ld = languageDataMap[lang]; + ld.Language = lang; + lg->GetTargetCompileFlags(target, config, lang, ld.Flags); + std::set<std::string> defines; + lg->GetTargetDefines(target, config, lang, defines); + ld.SetDefines(defines); + std::vector<std::string> includePathList; + lg->GetIncludeDirectories(includePathList, target, lang, config, true); + for (std::string const& i : includePathList) { + ld.IncludePathList.push_back( + std::make_pair(i, target->IsSystemIncludeDirectory(i, config, lang))); + } + } + + if (target->Target->GetProperty("INTERFACE_SOURCES") != nullptr) { + // Create an entry in the languageDataMap for interface sources. + CreateInterfaceSourcesEntry(lg, target, config, languageDataMap); + } + + Json::Value sourceGroupsValue = + DumpSourceFilesList(target, config, languageDataMap); + if (!sourceGroupsValue.empty()) { + result[kFILE_GROUPS_KEY] = sourceGroupsValue; + } + + return result; +} + +static Json::Value DumpTargetsList( + const std::vector<cmLocalGenerator*>& generators, const std::string& config) +{ + Json::Value result = Json::arrayValue; + + std::vector<cmGeneratorTarget*> targetList; + for (auto const& lgIt : generators) { + const auto& list = lgIt->GetGeneratorTargets(); + targetList.insert(targetList.end(), list.begin(), list.end()); + } + std::sort(targetList.begin(), targetList.end()); + + for (cmGeneratorTarget* target : targetList) { + Json::Value tmp = DumpTarget(target, config); + if (!tmp.isNull()) { + result.append(tmp); + } + } + + return result; +} + +static Json::Value DumpProjectList(const cmake* cm, std::string const& config) +{ + Json::Value result = Json::arrayValue; + + auto globalGen = cm->GetGlobalGenerator(); + + for (auto const& projectIt : globalGen->GetProjectMap()) { + Json::Value pObj = Json::objectValue; + pObj[kNAME_KEY] = projectIt.first; + + // All Projects must have at least one local generator + assert(!projectIt.second.empty()); + const cmLocalGenerator* lg = projectIt.second.at(0); + + // Project structure information: + const cmMakefile* mf = lg->GetMakefile(); + auto minVersion = mf->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); + pObj[kMINIMUM_CMAKE_VERSION] = minVersion ? minVersion : ""; + pObj[kSOURCE_DIRECTORY_KEY] = mf->GetCurrentSourceDirectory(); + pObj[kBUILD_DIRECTORY_KEY] = mf->GetCurrentBinaryDirectory(); + pObj[kTARGETS_KEY] = DumpTargetsList(projectIt.second, config); + + // For a project-level install rule it might be defined in any of its + // associated generators. + bool hasInstallRule = false; + for (const auto generator : projectIt.second) { + hasInstallRule = + generator->GetMakefile()->GetInstallGenerators().empty() == false; + + if (hasInstallRule) { + break; + } + } + + pObj[kHAS_INSTALL_RULE] = hasInstallRule; + + result.append(pObj); + } + + return result; +} + +static Json::Value DumpConfiguration(const cmake* cm, + const std::string& config) +{ + Json::Value result = Json::objectValue; + result[kNAME_KEY] = config; + + result[kPROJECTS_KEY] = DumpProjectList(cm, config); + + return result; +} + +static Json::Value DumpConfigurationsList(const cmake* cm) +{ + Json::Value result = Json::arrayValue; + + for (std::string const& c : getConfigurations(cm)) { + result.append(DumpConfiguration(cm, c)); + } + + return result; +} + +Json::Value cmDumpCodeModel(const cmake* cm) +{ + Json::Value result = Json::objectValue; + result[kCONFIGURATIONS_KEY] = DumpConfigurationsList(cm); + return result; +} diff --git a/Source/cmJsonObjects.h b/Source/cmJsonObjects.h new file mode 100644 index 0000000..cd4da94 --- /dev/null +++ b/Source/cmJsonObjects.h @@ -0,0 +1,27 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmJsonObjects_h +#define cmJsonObjects_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_jsoncpp_value.h" + +#include <string> +#include <vector> + +class cmake; +class cmGlobalGenerator; + +extern void cmGetCMakeInputs(const cmGlobalGenerator* gg, + const std::string& sourceDir, + const std::string& buildDir, + std::vector<std::string>* internalFiles, + std::vector<std::string>* explicitFiles, + std::vector<std::string>* tmpFiles); + +extern Json::Value cmDumpCodeModel(const cmake* cm); +extern Json::Value cmDumpCTestInfo(const cmake* cm); +extern Json::Value cmDumpCMakeInputs(const cmake* cm); + +#endif diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx index 1371c53..10425fd 100644 --- a/Source/cmLinkDirectoriesCommand.cxx +++ b/Source/cmLinkDirectoriesCommand.cxx @@ -4,6 +4,8 @@ #include <sstream> +#include "cmAlgorithms.h" +#include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmPolicies.h" #include "cmSystemTools.h" @@ -19,17 +21,34 @@ bool cmLinkDirectoriesCommand::InitialPass( return true; } - for (std::string const& i : args) { - this->AddLinkDir(i); + bool before = this->Makefile->IsOn("CMAKE_LINK_DIRECTORIES_BEFORE"); + + auto i = args.cbegin(); + if ((*i) == "BEFORE") { + before = true; + ++i; + } else if ((*i) == "AFTER") { + before = false; + ++i; + } + + std::vector<std::string> directories; + for (; i != args.cend(); ++i) { + this->AddLinkDir(*i, directories); } + + this->Makefile->AddLinkDirectory(cmJoin(directories, ";"), before); + return true; } -void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir) +void cmLinkDirectoriesCommand::AddLinkDir( + std::string const& dir, std::vector<std::string>& directories) { std::string unixPath = dir; cmSystemTools::ConvertToUnixSlashes(unixPath); - if (!cmSystemTools::FileIsFullPath(unixPath)) { + if (!cmSystemTools::FileIsFullPath(unixPath) && + !cmGeneratorExpression::StartsWithGeneratorExpression(unixPath)) { bool convertToAbsolute = false; std::ostringstream e; /* clang-format off */ @@ -41,6 +60,7 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir) case cmPolicies::WARN: e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0015); this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, e.str()); + break; case cmPolicies::OLD: // OLD behavior does not convert break; @@ -61,5 +81,5 @@ void cmLinkDirectoriesCommand::AddLinkDir(std::string const& dir) unixPath = tmp; } } - this->Makefile->AppendProperty("LINK_DIRECTORIES", unixPath.c_str()); + directories.push_back(unixPath); } diff --git a/Source/cmLinkDirectoriesCommand.h b/Source/cmLinkDirectoriesCommand.h index 3fd4e50..ae4fb7f 100644 --- a/Source/cmLinkDirectoriesCommand.h +++ b/Source/cmLinkDirectoriesCommand.h @@ -36,7 +36,8 @@ public: cmExecutionStatus& status) override; private: - void AddLinkDir(std::string const& dir); + void AddLinkDir(std::string const& dir, + std::vector<std::string>& directories); }; #endif diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index b468257..4d7e1e2 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -11,6 +11,7 @@ #include <algorithm> #include <assert.h> +#include <memory> #include <sstream> cmCommandContext::cmCommandName& cmCommandContext::cmCommandName::operator=( @@ -285,91 +286,66 @@ bool cmListFileParser::AddArgument(cmListFileLexer_Token* token, return true; } -struct cmListFileBacktrace::Entry : public cmListFileContext +// We hold either the bottom scope of a directory or a call/file context. +// Discriminate these cases via the parent pointer. +struct cmListFileBacktrace::Entry { - Entry(cmListFileContext const& lfc, Entry* up) - : cmListFileContext(lfc) - , Up(up) - , RefCount(0) + Entry(cmStateSnapshot bottom) + : Bottom(bottom) { - if (this->Up) { - this->Up->Ref(); - } } - ~Entry() + + Entry(std::shared_ptr<Entry const> parent, cmListFileContext lfc) + : Context(std::move(lfc)) + , Parent(std::move(parent)) { - if (this->Up) { - this->Up->Unref(); - } } - void Ref() { ++this->RefCount; } - void Unref() + + ~Entry() { - if (--this->RefCount == 0) { - delete this; + if (this->Parent) { + this->Context.~cmListFileContext(); + } else { + this->Bottom.~cmStateSnapshot(); } } - Entry* Up; - unsigned int RefCount; -}; -cmListFileBacktrace::cmListFileBacktrace(cmStateSnapshot const& bottom, - Entry* up, - cmListFileContext const& lfc) - : Bottom(bottom) - , Cur(new Entry(lfc, up)) -{ - assert(this->Bottom.IsValid()); - this->Cur->Ref(); -} - -cmListFileBacktrace::cmListFileBacktrace(cmStateSnapshot const& bottom, - Entry* cur) - : Bottom(bottom) - , Cur(cur) -{ - if (this->Cur) { - assert(this->Bottom.IsValid()); - this->Cur->Ref(); - } -} + bool IsBottom() const { return !this->Parent; } -cmListFileBacktrace::cmListFileBacktrace() - : Bottom() - , Cur(nullptr) -{ -} + union + { + cmStateSnapshot Bottom; + cmListFileContext Context; + }; + std::shared_ptr<Entry const> Parent; +}; cmListFileBacktrace::cmListFileBacktrace(cmStateSnapshot const& snapshot) - : Bottom(snapshot.GetCallStackBottom()) - , Cur(nullptr) + : TopEntry(std::make_shared<Entry const>(snapshot.GetCallStackBottom())) { } -cmListFileBacktrace::cmListFileBacktrace(cmListFileBacktrace const& r) - : Bottom(r.Bottom) - , Cur(r.Cur) +cmListFileBacktrace::cmListFileBacktrace(std::shared_ptr<Entry const> parent, + cmListFileContext const& lfc) + : TopEntry(std::make_shared<Entry const>(std::move(parent), lfc)) { - if (this->Cur) { - assert(this->Bottom.IsValid()); - this->Cur->Ref(); - } } -cmListFileBacktrace& cmListFileBacktrace::operator=( - cmListFileBacktrace const& r) +cmListFileBacktrace::cmListFileBacktrace(std::shared_ptr<Entry const> top) + : TopEntry(std::move(top)) { - cmListFileBacktrace tmp(r); - std::swap(this->Cur, tmp.Cur); - std::swap(this->Bottom, tmp.Bottom); - return *this; } -cmListFileBacktrace::~cmListFileBacktrace() +cmStateSnapshot cmListFileBacktrace::GetBottom() const { - if (this->Cur) { - this->Cur->Unref(); + cmStateSnapshot bottom; + if (Entry const* cur = this->TopEntry.get()) { + while (Entry const* parent = cur->Parent.get()) { + cur = parent; + } + bottom = cur->Bottom; } + return bottom; } cmListFileBacktrace cmListFileBacktrace::Push(std::string const& file) const @@ -380,54 +356,62 @@ cmListFileBacktrace cmListFileBacktrace::Push(std::string const& file) const // skipped during call stack printing. cmListFileContext lfc; lfc.FilePath = file; - return cmListFileBacktrace(this->Bottom, this->Cur, lfc); + return this->Push(lfc); } cmListFileBacktrace cmListFileBacktrace::Push( cmListFileContext const& lfc) const { - return cmListFileBacktrace(this->Bottom, this->Cur, lfc); + assert(this->TopEntry); + assert(!this->TopEntry->IsBottom() || this->TopEntry->Bottom.IsValid()); + return cmListFileBacktrace(this->TopEntry, lfc); } cmListFileBacktrace cmListFileBacktrace::Pop() const { - assert(this->Cur); - return cmListFileBacktrace(this->Bottom, this->Cur->Up); + assert(this->TopEntry); + assert(!this->TopEntry->IsBottom()); + return cmListFileBacktrace(this->TopEntry->Parent); } cmListFileContext const& cmListFileBacktrace::Top() const { - if (this->Cur) { - return *this->Cur; - } - static cmListFileContext const empty; - return empty; + assert(this->TopEntry); + assert(!this->TopEntry->IsBottom()); + return this->TopEntry->Context; } void cmListFileBacktrace::PrintTitle(std::ostream& out) const { - if (!this->Cur) { + // The title exists only if we have a call on top of the bottom. + if (!this->TopEntry || this->TopEntry->IsBottom()) { return; } - cmOutputConverter converter(this->Bottom); - cmListFileContext lfc = *this->Cur; - if (!this->Bottom.GetState()->GetIsInTryCompile()) { + cmListFileContext lfc = this->TopEntry->Context; + cmStateSnapshot bottom = this->GetBottom(); + cmOutputConverter converter(bottom); + if (!bottom.GetState()->GetIsInTryCompile()) { lfc.FilePath = converter.ConvertToRelativePath( - this->Bottom.GetState()->GetSourceDirectory(), lfc.FilePath); + bottom.GetState()->GetSourceDirectory(), lfc.FilePath); } out << (lfc.Line ? " at " : " in ") << lfc; } void cmListFileBacktrace::PrintCallStack(std::ostream& out) const { - if (!this->Cur || !this->Cur->Up) { + // The call stack exists only if we have at least two calls on top + // of the bottom. + if (!this->TopEntry || this->TopEntry->IsBottom() || + this->TopEntry->Parent->IsBottom()) { return; } bool first = true; - cmOutputConverter converter(this->Bottom); - for (Entry* i = this->Cur->Up; i; i = i->Up) { - if (i->Name.empty()) { + cmStateSnapshot bottom = this->GetBottom(); + cmOutputConverter converter(bottom); + for (Entry const* cur = this->TopEntry->Parent.get(); !cur->IsBottom(); + cur = cur->Parent.get()) { + if (cur->Context.Name.empty()) { // Skip this whole-file scope. When we get here we already will // have printed a more-specific context within the file. continue; @@ -436,10 +420,10 @@ void cmListFileBacktrace::PrintCallStack(std::ostream& out) const first = false; out << "Call Stack (most recent call first):\n"; } - cmListFileContext lfc = *i; - if (!this->Bottom.GetState()->GetIsInTryCompile()) { + cmListFileContext lfc = cur->Context; + if (!bottom.GetState()->GetIsInTryCompile()) { lfc.FilePath = converter.ConvertToRelativePath( - this->Bottom.GetState()->GetSourceDirectory(), lfc.FilePath); + bottom.GetState()->GetSourceDirectory(), lfc.FilePath); } out << " " << lfc << "\n"; } @@ -448,16 +432,19 @@ void cmListFileBacktrace::PrintCallStack(std::ostream& out) const size_t cmListFileBacktrace::Depth() const { size_t depth = 0; - if (this->Cur == nullptr) { - return 0; - } - - for (Entry* i = this->Cur->Up; i; i = i->Up) { - depth++; + if (Entry const* cur = this->TopEntry.get()) { + for (; !cur->IsBottom(); cur = cur->Parent.get()) { + ++depth; + } } return depth; } +bool cmListFileBacktrace::Empty() const +{ + return !this->TopEntry || this->TopEntry->IsBottom(); +} + std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc) { os << lfc.FilePath; diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 70f7166..3d3afdf 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <iosfwd> +#include <memory> // IWYU pragma: keep #include <stddef.h> #include <string> #include <vector> @@ -115,18 +116,22 @@ public: // Default-constructed backtrace may not be used until after // set via assignment from a backtrace constructed with a // valid snapshot. - cmListFileBacktrace(); + cmListFileBacktrace() = default; // Construct an empty backtrace whose bottom sits in the directory // indicated by the given valid snapshot. cmListFileBacktrace(cmStateSnapshot const& snapshot); - // Backtraces may be copied and assigned as values. - cmListFileBacktrace(cmListFileBacktrace const& r); - cmListFileBacktrace& operator=(cmListFileBacktrace const& r); - ~cmListFileBacktrace(); + // Backtraces may be copied, moved, and assigned as values. + cmListFileBacktrace(cmListFileBacktrace const&) = default; + cmListFileBacktrace(cmListFileBacktrace&&) // NOLINT(clang-tidy) + noexcept = default; + cmListFileBacktrace& operator=(cmListFileBacktrace const&) = default; + cmListFileBacktrace& operator=(cmListFileBacktrace&&) // NOLINT(clang-tidy) + noexcept = default; + ~cmListFileBacktrace() = default; - cmStateSnapshot GetBottom() const { return this->Bottom; } + cmStateSnapshot GetBottom() const; // Get a backtrace with the given file scope added to the top. // May not be called until after construction with a valid snapshot. @@ -141,7 +146,7 @@ public: cmListFileBacktrace Pop() const; // Get the context at the top of the backtrace. - // Returns an empty context if the backtrace is empty. + // This may be called only if Empty() would return false. cmListFileContext const& Top() const; // Print the top of the backtrace. @@ -153,14 +158,15 @@ public: // Get the number of 'frames' in this backtrace size_t Depth() const; + // Return true if this backtrace is empty. + bool Empty() const; + private: struct Entry; - - cmStateSnapshot Bottom; - Entry* Cur; - cmListFileBacktrace(cmStateSnapshot const& bottom, Entry* up, + std::shared_ptr<Entry const> TopEntry; + cmListFileBacktrace(std::shared_ptr<Entry const> parent, cmListFileContext const& lfc); - cmListFileBacktrace(cmStateSnapshot const& bottom, Entry* cur); + cmListFileBacktrace(std::shared_ptr<Entry const> top); }; struct cmListFile diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 354da4e..8d163b7 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -243,6 +243,17 @@ cmBacktraceRange cmMakefile::GetLinkOptionsBacktraces() const return this->StateSnapshot.GetDirectory().GetLinkOptionsEntryBacktraces(); } +cmStringRange cmMakefile::GetLinkDirectoriesEntries() const +{ + return this->StateSnapshot.GetDirectory().GetLinkDirectoriesEntries(); +} + +cmBacktraceRange cmMakefile::GetLinkDirectoriesBacktraces() const +{ + return this->StateSnapshot.GetDirectory() + .GetLinkDirectoriesEntryBacktraces(); +} + cmListFileBacktrace cmMakefile::GetBacktrace() const { return this->Backtrace; @@ -1237,6 +1248,18 @@ void cmMakefile::AddLinkOption(std::string const& option) this->AppendProperty("LINK_OPTIONS", option.c_str()); } +void cmMakefile::AddLinkDirectory(std::string const& directory, bool before) +{ + cmListFileBacktrace lfbt = this->GetBacktrace(); + if (before) { + this->StateSnapshot.GetDirectory().PrependLinkDirectoriesEntry(directory, + lfbt); + } else { + this->StateSnapshot.GetDirectory().AppendLinkDirectoriesEntry(directory, + lfbt); + } +} + bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove) { // Create a regular expression to match valid definitions. @@ -1335,10 +1358,6 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent) // link libraries this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES")); - // link directories - this->SetProperty("LINK_DIRECTORIES", - parent->GetProperty("LINK_DIRECTORIES")); - // the initial project name this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName()); @@ -1872,17 +1891,6 @@ void cmMakefile::AddGlobalLinkInformation(cmTarget& target) return; default:; } - if (const char* linkDirsProp = this->GetProperty("LINK_DIRECTORIES")) { - std::vector<std::string> linkDirs; - cmSystemTools::ExpandListArgument(linkDirsProp, linkDirs); - - for (std::string& linkDir : linkDirs) { - // Sanitize the path the same way the link_directories command does - // in case projects set the LINK_DIRECTORIES property directly. - cmSystemTools::ConvertToUnixSlashes(linkDir); - target.AddLinkDirectory(linkDir); - } - } if (const char* linkLibsProp = this->GetProperty("LINK_LIBRARIES")) { std::vector<std::string> linkLibs; diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index bb01c0b..b30f281 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -182,6 +182,7 @@ public: void AddCompileDefinition(std::string const& definition); void AddCompileOption(std::string const& option); void AddLinkOption(std::string const& option); + void AddLinkDirectory(std::string const& directory, bool before = false); /** Create a new imported target with the name and type given. */ cmTarget* AddImportedTarget(const std::string& name, @@ -802,6 +803,8 @@ public: cmBacktraceRange GetCompileDefinitionsBacktraces() const; cmStringRange GetLinkOptionsEntries() const; cmBacktraceRange GetLinkOptionsBacktraces() const; + cmStringRange GetLinkDirectoriesEntries() const; + cmBacktraceRange GetLinkDirectoriesBacktraces() const; std::set<std::string> const& GetSystemIncludeDirectories() const { diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index f99cc0f..a367e47 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -237,7 +237,10 @@ class cmMakefile; 13, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0080, \ "BundleUtilities cannot be included at configure time", 3, 13, 0, \ - cmPolicies::WARN) + cmPolicies::WARN) \ + SELECT(POLICY, CMP0081, \ + "Relative paths not allowed in LINK_DIRECTORIES target property.", \ + 3, 13, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -263,7 +266,8 @@ class cmMakefile; F(CMP0068) \ F(CMP0069) \ F(CMP0073) \ - F(CMP0076) + F(CMP0076) \ + F(CMP0081) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 1b04ca2..f15a14a 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -5,6 +5,7 @@ #include "cmAlgorithms.h" #include "cmConnection.h" #include "cmFileMonitor.h" +#include "cmJsonObjectDictionary.h" #include "cmServerDictionary.h" #include "cmServerProtocol.h" #include "cmSystemTools.h" diff --git a/Source/cmServerDictionary.h b/Source/cmServerDictionary.h index ebf16eb..961e4b7 100644 --- a/Source/cmServerDictionary.h +++ b/Source/cmServerDictionary.h @@ -25,78 +25,40 @@ static const std::string kSET_GLOBAL_SETTINGS_TYPE = "setGlobalSettings"; static const std::string kSIGNAL_TYPE = "signal"; static const std::string kCTEST_INFO_TYPE = "ctestInfo"; -static const std::string kARTIFACTS_KEY = "artifacts"; -static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; static const std::string kBUILD_FILES_KEY = "buildFiles"; static const std::string kCACHE_ARGUMENTS_KEY = "cacheArguments"; static const std::string kCACHE_KEY = "cache"; static const std::string kCAPABILITIES_KEY = "capabilities"; static const std::string kCHECK_SYSTEM_VARS_KEY = "checkSystemVars"; static const std::string kCMAKE_ROOT_DIRECTORY_KEY = "cmakeRootDirectory"; -static const std::string kCOMPILE_FLAGS_KEY = "compileFlags"; -static const std::string kCONFIGURATIONS_KEY = "configurations"; static const std::string kCOOKIE_KEY = "cookie"; static const std::string kDEBUG_OUTPUT_KEY = "debugOutput"; -static const std::string kDEFINES_KEY = "defines"; static const std::string kERROR_MESSAGE_KEY = "errorMessage"; static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; -static const std::string kFILE_GROUPS_KEY = "fileGroups"; -static const std::string kFRAMEWORK_PATH_KEY = "frameworkPath"; -static const std::string kFULL_NAME_KEY = "fullName"; static const std::string kGENERATOR_KEY = "generator"; -static const std::string kINCLUDE_PATH_KEY = "includePath"; -static const std::string kIS_CMAKE_KEY = "isCMake"; static const std::string kIS_EXPERIMENTAL_KEY = "isExperimental"; -static const std::string kIS_GENERATED_KEY = "isGenerated"; -static const std::string kIS_SYSTEM_KEY = "isSystem"; -static const std::string kIS_TEMPORARY_KEY = "isTemporary"; -static const std::string kKEY_KEY = "key"; static const std::string kKEYS_KEY = "keys"; -static const std::string kLANGUAGE_KEY = "language"; -static const std::string kLINKER_LANGUAGE_KEY = "linkerLanguage"; -static const std::string kLINK_FLAGS_KEY = "linkFlags"; -static const std::string kLINK_LANGUAGE_FLAGS_KEY = "linkLanguageFlags"; -static const std::string kLINK_LIBRARIES_KEY = "linkLibraries"; -static const std::string kLINK_PATH_KEY = "linkPath"; static const std::string kMAJOR_KEY = "major"; static const std::string kMESSAGE_KEY = "message"; static const std::string kMINOR_KEY = "minor"; -static const std::string kNAME_KEY = "name"; -static const std::string kPATH_KEY = "path"; static const std::string kPLATFORM_KEY = "platform"; static const std::string kPROGRESS_CURRENT_KEY = "progressCurrent"; static const std::string kPROGRESS_MAXIMUM_KEY = "progressMaximum"; static const std::string kPROGRESS_MESSAGE_KEY = "progressMessage"; static const std::string kPROGRESS_MINIMUM_KEY = "progressMinimum"; -static const std::string kPROJECTS_KEY = "projects"; -static const std::string kPROPERTIES_KEY = "properties"; static const std::string kPROTOCOL_VERSION_KEY = "protocolVersion"; static const std::string kREPLY_TO_KEY = "inReplyTo"; -static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; -static const std::string kSOURCES_KEY = "sources"; static const std::string kSUPPORTED_PROTOCOL_VERSIONS = "supportedProtocolVersions"; -static const std::string kSYSROOT_KEY = "sysroot"; -static const std::string kTARGETS_KEY = "targets"; static const std::string kTITLE_KEY = "title"; static const std::string kTOOLSET_KEY = "toolset"; static const std::string kTRACE_EXPAND_KEY = "traceExpand"; static const std::string kTRACE_KEY = "trace"; -static const std::string kTYPE_KEY = "type"; -static const std::string kVALUE_KEY = "value"; static const std::string kWARN_UNINITIALIZED_KEY = "warnUninitialized"; static const std::string kWARN_UNUSED_CLI_KEY = "warnUnusedCli"; static const std::string kWARN_UNUSED_KEY = "warnUnused"; static const std::string kWATCHED_DIRECTORIES_KEY = "watchedDirectories"; static const std::string kWATCHED_FILES_KEY = "watchedFiles"; -static const std::string kHAS_INSTALL_RULE = "hasInstallRule"; -static const std::string kINSTALL_PATHS = "installPaths"; -static const std::string kCTEST_NAME = "ctestName"; -static const std::string kCTEST_COMMAND = "ctestCommand"; -static const std::string kCTEST_INFO = "ctestInfo"; -static const std::string kMINIMUM_CMAKE_VERSION = "minimumCMakeVersion"; -static const std::string kIS_GENERATOR_PROVIDED_KEY = "isGeneratorProvided"; -static const std::string kIS_INTERFACE_SOURCES_KEY = "isInterfaceSources"; static const std::string kSTART_MAGIC = "[== \"CMake Server\" ==["; static const std::string kEND_MAGIC = "]== \"CMake Server\" ==]"; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 2cad657..f75a5ce 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -5,38 +5,21 @@ #include "cmAlgorithms.h" #include "cmExternalMakefileProjectGenerator.h" #include "cmFileMonitor.h" -#include "cmGeneratorExpression.h" -#include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" -#include "cmInstallGenerator.h" -#include "cmInstallTargetGenerator.h" -#include "cmLinkLineComputer.h" -#include "cmLocalGenerator.h" -#include "cmMakefile.h" -#include "cmProperty.h" +#include "cmJsonObjectDictionary.h" +#include "cmJsonObjects.h" #include "cmServer.h" #include "cmServerDictionary.h" -#include "cmSourceFile.h" #include "cmState.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" -#include "cmStateTypes.h" #include "cmSystemTools.h" -#include "cmTarget.h" -#include "cmTest.h" #include "cm_uv.h" #include "cmake.h" #include <algorithm> #include <cassert> -#include <cstddef> #include <functional> -#include <limits> -#include <map> #include <memory> -#include <set> #include <string> -#include <unordered_map> #include <vector> // Get rid of some windows macros: @@ -44,38 +27,6 @@ namespace { -std::vector<std::string> getConfigurations(const cmake* cm) -{ - std::vector<std::string> configurations; - auto makefiles = cm->GetGlobalGenerator()->GetMakefiles(); - if (makefiles.empty()) { - return configurations; - } - - makefiles[0]->GetConfigurations(configurations); - if (configurations.empty()) { - configurations.push_back(""); - } - return configurations; -} - -bool hasString(const Json::Value& v, const std::string& s) -{ - return !v.isNull() && - std::any_of(v.begin(), v.end(), - [s](const Json::Value& i) { return i.asString() == s; }); -} - -template <class T> -Json::Value fromStringList(const T& in) -{ - Json::Value result = Json::arrayValue; - for (std::string const& i : in) { - result.append(i); - } - return result; -} - std::vector<std::string> toStringList(const Json::Value& in) { std::vector<std::string> result; @@ -85,49 +36,6 @@ std::vector<std::string> toStringList(const Json::Value& in) return result; } -void getCMakeInputs(const cmGlobalGenerator* gg, const std::string& sourceDir, - const std::string& buildDir, - std::vector<std::string>* internalFiles, - std::vector<std::string>* explicitFiles, - std::vector<std::string>* tmpFiles) -{ - const std::string cmakeRootDir = cmSystemTools::GetCMakeRoot() + '/'; - std::vector<cmMakefile*> const& makefiles = gg->GetMakefiles(); - for (cmMakefile const* mf : makefiles) { - for (std::string const& lf : mf->GetListFiles()) { - - const std::string startOfFile = lf.substr(0, cmakeRootDir.size()); - const bool isInternal = (startOfFile == cmakeRootDir); - const bool isTemporary = !isInternal && (lf.find(buildDir + '/') == 0); - - std::string toAdd = lf; - if (!sourceDir.empty()) { - const std::string& relative = - cmSystemTools::RelativePath(sourceDir, lf); - if (toAdd.size() > relative.size()) { - toAdd = relative; - } - } - - if (isInternal) { - if (internalFiles) { - internalFiles->push_back(std::move(toAdd)); - } - } else { - if (isTemporary) { - if (tmpFiles) { - tmpFiles->push_back(std::move(toAdd)); - } - } else { - if (explicitFiles) { - explicitFiles->push_back(std::move(toAdd)); - } - } - } - } - } -} - } // namespace cmServerRequest::cmServerRequest(cmServer* server, cmConnection* connection, @@ -558,704 +466,16 @@ cmServerResponse cmServerProtocol1::ProcessCMakeInputs( } const cmake* cm = this->CMakeInstance(); - const cmGlobalGenerator* gg = cm->GetGlobalGenerator(); const std::string cmakeRootDir = cmSystemTools::GetCMakeRoot(); - const std::string& buildDir = cm->GetHomeOutputDirectory(); const std::string& sourceDir = cm->GetHomeDirectory(); Json::Value result = Json::objectValue; result[kSOURCE_DIRECTORY_KEY] = sourceDir; result[kCMAKE_ROOT_DIRECTORY_KEY] = cmakeRootDir; - - std::vector<std::string> internalFiles; - std::vector<std::string> explicitFiles; - std::vector<std::string> tmpFiles; - getCMakeInputs(gg, sourceDir, buildDir, &internalFiles, &explicitFiles, - &tmpFiles); - - Json::Value array = Json::arrayValue; - - Json::Value tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = true; - tmp[kIS_TEMPORARY_KEY] = false; - tmp[kSOURCES_KEY] = fromStringList(internalFiles); - array.append(tmp); - - tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = false; - tmp[kIS_TEMPORARY_KEY] = false; - tmp[kSOURCES_KEY] = fromStringList(explicitFiles); - array.append(tmp); - - tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = false; - tmp[kIS_TEMPORARY_KEY] = true; - tmp[kSOURCES_KEY] = fromStringList(tmpFiles); - array.append(tmp); - - result[kBUILD_FILES_KEY] = array; - + result[kBUILD_FILES_KEY] = cmDumpCMakeInputs(cm); return request.Reply(result); } -const std::string kInterfaceSourcesLanguageDataKey = - "INTERFACE_SOURCES_LD_KEY"; -class LanguageData -{ -public: - bool operator==(const LanguageData& other) const; - - void SetDefines(const std::set<std::string>& defines); - - bool IsGenerated = false; - std::string Language; - std::string Flags; - std::vector<std::string> Defines; - std::vector<std::pair<std::string, bool>> IncludePathList; -}; - -bool LanguageData::operator==(const LanguageData& other) const -{ - return Language == other.Language && Defines == other.Defines && - Flags == other.Flags && IncludePathList == other.IncludePathList && - IsGenerated == other.IsGenerated; -} - -void LanguageData::SetDefines(const std::set<std::string>& defines) -{ - std::vector<std::string> result; - result.reserve(defines.size()); - for (std::string const& i : defines) { - result.push_back(i); - } - std::sort(result.begin(), result.end()); - Defines = std::move(result); -} - -struct FileGroupSources -{ - bool IsInterfaceSources; - std::vector<std::string> Files; -}; - -namespace std { - -template <> -struct hash<LanguageData> -{ - std::size_t operator()(const LanguageData& in) const - { - using std::hash; - size_t result = - hash<std::string>()(in.Language) ^ hash<std::string>()(in.Flags); - for (auto const& i : in.IncludePathList) { - result = result ^ - (hash<std::string>()(i.first) ^ - (i.second ? std::numeric_limits<size_t>::max() : 0)); - } - for (auto const& i : in.Defines) { - result = result ^ hash<std::string>()(i); - } - result = - result ^ (in.IsGenerated ? std::numeric_limits<size_t>::max() : 0); - return result; - } -}; - -} // namespace std - -static Json::Value DumpSourceFileGroup(const LanguageData& data, - bool isInterfaceSource, - const std::vector<std::string>& files, - const std::string& baseDir) -{ - Json::Value result = Json::objectValue; - - if (isInterfaceSource) { - result[kIS_INTERFACE_SOURCES_KEY] = true; - } - if (!data.Language.empty()) { - result[kLANGUAGE_KEY] = data.Language; - } - if (!data.Flags.empty()) { - result[kCOMPILE_FLAGS_KEY] = data.Flags; - } - if (!data.IncludePathList.empty()) { - Json::Value includes = Json::arrayValue; - for (auto const& i : data.IncludePathList) { - Json::Value tmp = Json::objectValue; - tmp[kPATH_KEY] = i.first; - if (i.second) { - tmp[kIS_SYSTEM_KEY] = i.second; - } - includes.append(tmp); - } - result[kINCLUDE_PATH_KEY] = includes; - } - if (!data.Defines.empty()) { - result[kDEFINES_KEY] = fromStringList(data.Defines); - } - - result[kIS_GENERATED_KEY] = data.IsGenerated; - - Json::Value sourcesValue = Json::arrayValue; - for (auto const& i : files) { - const std::string relPath = cmSystemTools::RelativePath(baseDir, i); - sourcesValue.append(relPath.size() < i.size() ? relPath : i); - } - - result[kSOURCES_KEY] = sourcesValue; - return result; -} - -static void PopulateFileGroupData( - cmGeneratorTarget* target, bool isInterfaceSources, - const std::vector<cmSourceFile*>& files, const std::string& config, - const std::map<std::string, LanguageData>& languageDataMap, - std::unordered_map<LanguageData, FileGroupSources>& fileGroups) -{ - for (cmSourceFile* file : files) { - LanguageData fileData; - fileData.Language = file->GetLanguage(); - if (!fileData.Language.empty() || isInterfaceSources) { - const LanguageData& ld = isInterfaceSources - ? languageDataMap.at(kInterfaceSourcesLanguageDataKey) - : languageDataMap.at(fileData.Language); - cmLocalGenerator* lg = target->GetLocalGenerator(); - cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target, - fileData.Language); - - std::string compileFlags = ld.Flags; - const std::string COMPILE_FLAGS("COMPILE_FLAGS"); - if (const char* cflags = file->GetProperty(COMPILE_FLAGS)) { - lg->AppendFlags(compileFlags, - genexInterpreter.Evaluate(cflags, COMPILE_FLAGS)); - } - const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); - if (const char* coptions = file->GetProperty(COMPILE_OPTIONS)) { - lg->AppendCompileOptions( - compileFlags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); - } - fileData.Flags = compileFlags; - - // Add include directories from source file properties. - std::vector<std::string> includes; - - const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); - if (const char* cincludes = file->GetProperty(INCLUDE_DIRECTORIES)) { - const std::string& evaluatedIncludes = - genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES); - lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file); - - for (const auto& include : includes) { - // INTERFACE_LIBRARY targets do not support the - // IsSystemIncludeDirectory call so just set it to false. - const bool isSystemInclude = isInterfaceSources - ? false - : target->IsSystemIncludeDirectory(include, config, - fileData.Language); - fileData.IncludePathList.push_back( - std::make_pair(include, isSystemInclude)); - } - } - - fileData.IncludePathList.insert(fileData.IncludePathList.end(), - ld.IncludePathList.begin(), - ld.IncludePathList.end()); - - const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); - std::set<std::string> defines; - if (const char* defs = file->GetProperty(COMPILE_DEFINITIONS)) { - lg->AppendDefines( - defines, genexInterpreter.Evaluate(defs, COMPILE_DEFINITIONS)); - } - - const std::string defPropName = - "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); - if (const char* config_defs = file->GetProperty(defPropName)) { - lg->AppendDefines( - defines, - genexInterpreter.Evaluate(config_defs, COMPILE_DEFINITIONS)); - } - - defines.insert(ld.Defines.begin(), ld.Defines.end()); - - fileData.SetDefines(defines); - } - - fileData.IsGenerated = file->GetPropertyAsBool("GENERATED"); - FileGroupSources& groupFileList = fileGroups[fileData]; - groupFileList.IsInterfaceSources = isInterfaceSources; - groupFileList.Files.push_back(file->GetFullPath()); - } -} - -static Json::Value DumpSourceFilesList( - cmGeneratorTarget* target, const std::string& config, - const std::map<std::string, LanguageData>& languageDataMap) -{ - const cmStateEnums::TargetType type = target->GetType(); - std::unordered_map<LanguageData, FileGroupSources> fileGroups; - - // Collect sourcefile groups: - - std::vector<cmSourceFile*> files; - if (type == cmStateEnums::INTERFACE_LIBRARY) { - // INTERFACE_LIBRARY targets do not create all the data structures - // associated with regular targets. If properties are explicitly specified - // for files in INTERFACE_SOURCES then we can get them through the Makefile - // rather than the target. - files = target->Makefile->GetSourceFiles(); - } else { - target->GetSourceFiles(files, config); - PopulateFileGroupData(target, false /* isInterfaceSources */, files, - config, languageDataMap, fileGroups); - } - - // Collect interface sourcefile groups: - - auto targetProp = target->Target->GetProperty("INTERFACE_SOURCES"); - if (targetProp != nullptr) { - cmGeneratorExpressionInterpreter genexInterpreter( - target->GetLocalGenerator(), config, target); - - auto evaluatedSources = cmsys::SystemTools::SplitString( - genexInterpreter.Evaluate(targetProp, "INTERFACE_SOURCES"), ';'); - - std::map<std::string, cmSourceFile*> filesMap; - for (auto file : files) { - filesMap[file->GetFullPath()] = file; - } - - std::vector<cmSourceFile*> interfaceSourceFiles; - for (const std::string& interfaceSourceFilePath : evaluatedSources) { - auto entry = filesMap.find(interfaceSourceFilePath); - if (entry != filesMap.end()) { - // use what we have since it has all the associated properties - interfaceSourceFiles.push_back(entry->second); - } else { - interfaceSourceFiles.push_back( - new cmSourceFile(target->Makefile, interfaceSourceFilePath)); - } - } - - PopulateFileGroupData(target, true /* isInterfaceSources */, - interfaceSourceFiles, config, languageDataMap, - fileGroups); - } - - const std::string& baseDir = target->Makefile->GetCurrentSourceDirectory(); - Json::Value result = Json::arrayValue; - for (auto const& it : fileGroups) { - Json::Value group = DumpSourceFileGroup( - it.first, it.second.IsInterfaceSources, it.second.Files, baseDir); - if (!group.isNull()) { - result.append(group); - } - } - - return result; -} - -static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kCTEST_NAME] = testInfo->GetName(); - - // Concat command entries together. After the first should be the arguments - // for the command - std::string command; - for (auto const& cmd : testInfo->GetCommand()) { - command.append(cmd); - command.append(" "); - } - - // Remove any config specific variables from the output. - cmGeneratorExpression ge; - auto cge = ge.Parse(command); - const std::string& processed = cge->Evaluate(lg, config); - result[kCTEST_COMMAND] = processed; - - // Build up the list of properties that may have been specified - Json::Value properties = Json::arrayValue; - for (auto& prop : testInfo->GetProperties()) { - 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()); - const std::string& processed_value = cge_value->Evaluate(lg, config); - entry[kVALUE_KEY] = processed_value; - properties.append(entry); - } - result[kPROPERTIES_KEY] = properties; - - return result; -} - -static void DumpMakefileTests(cmLocalGenerator* lg, const std::string& config, - Json::Value* result) -{ - auto mf = lg->GetMakefile(); - std::vector<cmTest*> tests; - mf->GetTests(config, tests); - for (auto test : tests) { - Json::Value tmp = DumpCTestInfo(lg, test, config); - if (!tmp.isNull()) { - result->append(tmp); - } - } -} - -static Json::Value DumpCTestProjectList(const cmake* cm, - std::string const& config) -{ - Json::Value result = Json::arrayValue; - - auto globalGen = cm->GetGlobalGenerator(); - - for (const auto& projectIt : globalGen->GetProjectMap()) { - Json::Value pObj = Json::objectValue; - pObj[kNAME_KEY] = projectIt.first; - - Json::Value tests = Json::arrayValue; - - // Gather tests for every generator - for (const auto& lg : projectIt.second) { - // Make sure they're generated. - lg->GenerateTestFiles(); - DumpMakefileTests(lg, config, &tests); - } - - pObj[kCTEST_INFO] = tests; - - result.append(pObj); - } - - return result; -} - -static Json::Value DumpCTestConfiguration(const cmake* cm, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kNAME_KEY] = config; - - result[kPROJECTS_KEY] = DumpCTestProjectList(cm, config); - - return result; -} - -static Json::Value DumpCTestConfigurationsList(const cmake* cm) -{ - Json::Value result = Json::arrayValue; - - for (const std::string& c : getConfigurations(cm)) { - result.append(DumpCTestConfiguration(cm, c)); - } - - return result; -} - -static void GetTargetProperty( - cmGeneratorExpressionInterpreter& genexInterpreter, - cmGeneratorTarget* target, const char* propertyName, - std::vector<std::string>& propertyValue) -{ - auto targetProp = target->Target->GetProperty(propertyName); - if (targetProp != nullptr) { - propertyValue = cmsys::SystemTools::SplitString( - genexInterpreter.Evaluate(targetProp, propertyName), ';'); - } -} - -static void CreateInterfaceSourcesEntry( - cmLocalGenerator* lg, cmGeneratorTarget* target, const std::string& config, - std::map<std::string, LanguageData>& languageDataMap) -{ - LanguageData& ld = languageDataMap[kInterfaceSourcesLanguageDataKey]; - ld.Language = ""; - - cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target); - std::vector<std::string> propertyValue; - GetTargetProperty(genexInterpreter, target, "INTERFACE_INCLUDE_DIRECTORIES", - propertyValue); - for (std::string const& i : propertyValue) { - ld.IncludePathList.push_back( - std::make_pair(i, false /* isSystemInclude */)); - } - - propertyValue.clear(); - GetTargetProperty(genexInterpreter, target, - "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", propertyValue); - for (std::string const& i : propertyValue) { - ld.IncludePathList.push_back( - std::make_pair(i, true /* isSystemInclude */)); - } - - propertyValue.clear(); - GetTargetProperty(genexInterpreter, target, "INTERFACE_COMPILE_OPTIONS", - propertyValue); - for (const auto& s : propertyValue) { - ld.Flags += " " + s; - } - - propertyValue.clear(); - GetTargetProperty(genexInterpreter, target, "INTERFACE_COMPILE_DEFINITIONS", - propertyValue); - if (!propertyValue.empty()) { - std::set<std::string> defines(propertyValue.begin(), propertyValue.end()); - ld.SetDefines(defines); - } -} - -static Json::Value DumpTarget(cmGeneratorTarget* target, - const std::string& config) -{ - cmLocalGenerator* lg = target->GetLocalGenerator(); - const cmState* state = lg->GetState(); - - const cmStateEnums::TargetType type = target->GetType(); - const std::string typeName = state->GetTargetTypeName(type); - - Json::Value ttl = Json::arrayValue; - ttl.append("EXECUTABLE"); - ttl.append("STATIC_LIBRARY"); - ttl.append("SHARED_LIBRARY"); - ttl.append("MODULE_LIBRARY"); - ttl.append("OBJECT_LIBRARY"); - ttl.append("UTILITY"); - ttl.append("INTERFACE_LIBRARY"); - - if (!hasString(ttl, typeName) || target->IsImported()) { - return Json::Value(); - } - - Json::Value result = Json::objectValue; - result[kNAME_KEY] = target->GetName(); - result[kIS_GENERATOR_PROVIDED_KEY] = - target->Target->GetIsGeneratorProvided(); - result[kTYPE_KEY] = typeName; - result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory(); - result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory(); - result[kFULL_NAME_KEY] = target->GetFullName(config); - - if (target->Target->GetHaveInstallRule()) { - result[kHAS_INSTALL_RULE] = true; - - Json::Value installPaths = Json::arrayValue; - auto targetGenerators = target->Makefile->GetInstallGenerators(); - for (auto installGenerator : targetGenerators) { - auto installTargetGenerator = - dynamic_cast<cmInstallTargetGenerator*>(installGenerator); - if (installTargetGenerator != nullptr && - installTargetGenerator->GetTarget()->Target == target->Target) { - auto dest = installTargetGenerator->GetDestination(config); - - std::string installPath; - if (!dest.empty() && cmSystemTools::FileIsFullPath(dest)) { - installPath = dest; - } else { - std::string installPrefix = - target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); - installPath = installPrefix + '/' + dest; - } - - installPaths.append(installPath); - } - } - - result[kINSTALL_PATHS] = installPaths; - } - - if (target->HaveWellDefinedOutputFiles()) { - Json::Value artifacts = Json::arrayValue; - artifacts.append( - target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact)); - if (target->IsDLLPlatform()) { - artifacts.append( - target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact)); - const cmGeneratorTarget::OutputInfo* output = - target->GetOutputInfo(config); - if (output && !output->PdbDir.empty()) { - artifacts.append(output->PdbDir + '/' + target->GetPDBName(config)); - } - } - result[kARTIFACTS_KEY] = artifacts; - - result[kLINKER_LANGUAGE_KEY] = target->GetLinkerLanguage(config); - - std::string linkLibs; - std::string linkFlags; - std::string linkLanguageFlags; - std::string frameworkPath; - std::string linkPath; - cmLinkLineComputer linkLineComputer(lg, - lg->GetStateSnapshot().GetDirectory()); - lg->GetTargetFlags(&linkLineComputer, config, linkLibs, linkLanguageFlags, - linkFlags, frameworkPath, linkPath, target); - - linkLibs = cmSystemTools::TrimWhitespace(linkLibs); - linkFlags = cmSystemTools::TrimWhitespace(linkFlags); - linkLanguageFlags = cmSystemTools::TrimWhitespace(linkLanguageFlags); - frameworkPath = cmSystemTools::TrimWhitespace(frameworkPath); - linkPath = cmSystemTools::TrimWhitespace(linkPath); - - if (!cmSystemTools::TrimWhitespace(linkLibs).empty()) { - result[kLINK_LIBRARIES_KEY] = linkLibs; - } - if (!cmSystemTools::TrimWhitespace(linkFlags).empty()) { - result[kLINK_FLAGS_KEY] = linkFlags; - } - if (!cmSystemTools::TrimWhitespace(linkLanguageFlags).empty()) { - result[kLINK_LANGUAGE_FLAGS_KEY] = linkLanguageFlags; - } - if (!frameworkPath.empty()) { - result[kFRAMEWORK_PATH_KEY] = frameworkPath; - } - if (!linkPath.empty()) { - result[kLINK_PATH_KEY] = linkPath; - } - const std::string sysroot = - lg->GetMakefile()->GetSafeDefinition("CMAKE_SYSROOT"); - if (!sysroot.empty()) { - result[kSYSROOT_KEY] = sysroot; - } - } - - std::set<std::string> languages; - std::map<std::string, LanguageData> languageDataMap; - if (type == cmStateEnums::INTERFACE_LIBRARY) { - // INTERFACE_LIBRARY targets do not create all the data structures - // associated with regular targets. If properties are explicitly specified - // for files in INTERFACE_SOURCES then we can get them through the Makefile - // rather than the target. - for (auto file : target->Makefile->GetSourceFiles()) { - const std::string& language = file->GetLanguage(); - if (!language.empty()) { - languages.insert(language); - } - } - } else { - target->GetLanguages(languages, config); - } - - for (std::string const& lang : languages) { - LanguageData& ld = languageDataMap[lang]; - ld.Language = lang; - lg->GetTargetCompileFlags(target, config, lang, ld.Flags); - std::set<std::string> defines; - lg->GetTargetDefines(target, config, lang, defines); - ld.SetDefines(defines); - std::vector<std::string> includePathList; - lg->GetIncludeDirectories(includePathList, target, lang, config, true); - for (std::string const& i : includePathList) { - ld.IncludePathList.push_back( - std::make_pair(i, target->IsSystemIncludeDirectory(i, config, lang))); - } - } - - if (target->Target->GetProperty("INTERFACE_SOURCES") != nullptr) { - // Create an entry in the languageDataMap for interface sources. - CreateInterfaceSourcesEntry(lg, target, config, languageDataMap); - } - - Json::Value sourceGroupsValue = - DumpSourceFilesList(target, config, languageDataMap); - if (!sourceGroupsValue.empty()) { - result[kFILE_GROUPS_KEY] = sourceGroupsValue; - } - - return result; -} - -static Json::Value DumpTargetsList( - const std::vector<cmLocalGenerator*>& generators, const std::string& config) -{ - Json::Value result = Json::arrayValue; - - std::vector<cmGeneratorTarget*> targetList; - for (auto const& lgIt : generators) { - const auto& list = lgIt->GetGeneratorTargets(); - targetList.insert(targetList.end(), list.begin(), list.end()); - } - std::sort(targetList.begin(), targetList.end()); - - for (cmGeneratorTarget* target : targetList) { - Json::Value tmp = DumpTarget(target, config); - if (!tmp.isNull()) { - result.append(tmp); - } - } - - return result; -} - -static Json::Value DumpProjectList(const cmake* cm, std::string const& config) -{ - Json::Value result = Json::arrayValue; - - auto globalGen = cm->GetGlobalGenerator(); - - for (auto const& projectIt : globalGen->GetProjectMap()) { - Json::Value pObj = Json::objectValue; - pObj[kNAME_KEY] = projectIt.first; - - // All Projects must have at least one local generator - assert(!projectIt.second.empty()); - const cmLocalGenerator* lg = projectIt.second.at(0); - - // Project structure information: - const cmMakefile* mf = lg->GetMakefile(); - auto minVersion = mf->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); - pObj[kMINIMUM_CMAKE_VERSION] = minVersion ? minVersion : ""; - pObj[kSOURCE_DIRECTORY_KEY] = mf->GetCurrentSourceDirectory(); - pObj[kBUILD_DIRECTORY_KEY] = mf->GetCurrentBinaryDirectory(); - pObj[kTARGETS_KEY] = DumpTargetsList(projectIt.second, config); - - // For a project-level install rule it might be defined in any of its - // associated generators. - bool hasInstallRule = false; - for (const auto generator : projectIt.second) { - hasInstallRule = - generator->GetMakefile()->GetInstallGenerators().empty() == false; - - if (hasInstallRule) { - break; - } - } - - pObj[kHAS_INSTALL_RULE] = hasInstallRule; - - result.append(pObj); - } - - return result; -} - -static Json::Value DumpConfiguration(const cmake* cm, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kNAME_KEY] = config; - - result[kPROJECTS_KEY] = DumpProjectList(cm, config); - - return result; -} - -static Json::Value DumpConfigurationsList(const cmake* cm) -{ - Json::Value result = Json::arrayValue; - - for (std::string const& c : getConfigurations(cm)) { - result.append(DumpConfiguration(cm, c)); - } - - return result; -} - cmServerResponse cmServerProtocol1::ProcessCodeModel( const cmServerRequest& request) { @@ -1263,9 +483,7 @@ cmServerResponse cmServerProtocol1::ProcessCodeModel( return request.ReportError("No build system was generated yet."); } - Json::Value result = Json::objectValue; - result[kCONFIGURATIONS_KEY] = DumpConfigurationsList(this->CMakeInstance()); - return request.Reply(result); + return request.Reply(cmDumpCodeModel(this->CMakeInstance())); } cmServerResponse cmServerProtocol1::ProcessCompute( @@ -1382,7 +600,8 @@ cmServerResponse cmServerProtocol1::ProcessConfigure( } std::vector<std::string> toWatchList; - getCMakeInputs(gg, std::string(), buildDir, nullptr, &toWatchList, nullptr); + cmGetCMakeInputs(gg, std::string(), buildDir, nullptr, &toWatchList, + nullptr); FileMonitor()->MonitorPaths(toWatchList, [this](const std::string& p, int e, int s) { @@ -1488,10 +707,7 @@ cmServerResponse cmServerProtocol1::ProcessCTests( return request.ReportError("This instance was not yet computed."); } - Json::Value result = Json::objectValue; - result[kCONFIGURATIONS_KEY] = - DumpCTestConfigurationsList(this->CMakeInstance()); - return request.Reply(result); + return request.Reply(cmDumpCTestInfo(this->CMakeInstance())); } cmServerProtocol1::GeneratorInformation::GeneratorInformation( diff --git a/Source/cmState.cxx b/Source/cmState.cxx index c6667f6..4b65cf1 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -284,6 +284,8 @@ cmStateSnapshot cmState::Reset() it->CompileOptionsBacktraces.clear(); it->LinkOptions.clear(); it->LinkOptionsBacktraces.clear(); + it->LinkDirectories.clear(); + it->LinkDirectoriesBacktraces.clear(); it->DirectoryEnd = pos; it->NormalTargetNames.clear(); it->Properties.clear(); @@ -660,6 +662,7 @@ cmStateSnapshot cmState::CreateBaseSnapshot() pos->CompileDefinitionsPosition = 0; pos->CompileOptionsPosition = 0; pos->LinkOptionsPosition = 0; + pos->LinkDirectoriesPosition = 0; pos->BuildSystemDirectory->DirectoryEnd = pos; pos->Policies = this->PolicyStack.Root(); pos->PolicyRoot = this->PolicyStack.Root(); @@ -813,6 +816,8 @@ cmStateSnapshot cmState::Pop(cmStateSnapshot const& originSnapshot) prevPos->BuildSystemDirectory->CompileOptions.size(); prevPos->LinkOptionsPosition = prevPos->BuildSystemDirectory->LinkOptions.size(); + prevPos->LinkDirectoriesPosition = + prevPos->BuildSystemDirectory->LinkDirectories.size(); prevPos->BuildSystemDirectory->DirectoryEnd = prevPos; if (!pos->Keep && this->SnapshotData.IsLast(pos)) { diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index 925b161..f94e714 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx @@ -396,6 +396,70 @@ void cmStateDirectory::ClearLinkOptions() this->Snapshot_.Position->LinkOptionsPosition); } +cmStringRange cmStateDirectory::GetLinkDirectoriesEntries() const +{ + return GetPropertyContent(this->DirectoryState->LinkDirectories, + this->Snapshot_.Position->LinkDirectoriesPosition); +} + +cmBacktraceRange cmStateDirectory::GetLinkDirectoriesEntryBacktraces() const +{ + return GetPropertyBacktraces( + this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition); +} + +void cmStateDirectory::AppendLinkDirectoriesEntry( + const std::string& vec, const cmListFileBacktrace& lfbt) +{ + AppendEntry(this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt); +} +void cmStateDirectory::PrependLinkDirectoriesEntry( + const std::string& vec, const cmListFileBacktrace& lfbt) +{ + std::vector<std::string>::iterator entryEnd = + this->DirectoryState->LinkDirectories.begin() + + this->Snapshot_.Position->LinkDirectoriesPosition; + + std::vector<std::string>::reverse_iterator rend = + this->DirectoryState->LinkDirectories.rend(); + std::vector<std::string>::reverse_iterator rbegin = + cmMakeReverseIterator(entryEnd); + rbegin = std::find(rbegin, rend, cmPropertySentinal); + + std::vector<std::string>::iterator entryIt = rbegin.base(); + std::vector<std::string>::iterator entryBegin = + this->DirectoryState->LinkDirectories.begin(); + + std::vector<cmListFileBacktrace>::iterator btIt = + this->DirectoryState->LinkDirectoriesBacktraces.begin() + + std::distance(entryBegin, entryIt); + + this->DirectoryState->LinkDirectories.insert(entryIt, vec); + this->DirectoryState->LinkDirectoriesBacktraces.insert(btIt, lfbt); + + this->Snapshot_.Position->LinkDirectoriesPosition = + this->DirectoryState->LinkDirectories.size(); +} + +void cmStateDirectory::SetLinkDirectories(const std::string& vec, + const cmListFileBacktrace& lfbt) +{ + SetContent(this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt); +} + +void cmStateDirectory::ClearLinkDirectories() +{ + ClearContent(this->DirectoryState->LinkDirectories, + this->DirectoryState->LinkDirectoriesBacktraces, + this->Snapshot_.Position->LinkDirectoriesPosition); +} + void cmStateDirectory::SetProperty(const std::string& prop, const char* value, cmListFileBacktrace const& lfbt) { @@ -431,6 +495,14 @@ void cmStateDirectory::SetProperty(const std::string& prop, const char* value, this->SetLinkOptions(value, lfbt); return; } + if (prop == "LINK_DIRECTORIES") { + if (!value) { + this->ClearLinkDirectories(); + return; + } + this->SetLinkDirectories(value, lfbt); + return; + } this->DirectoryState->Properties.SetProperty(prop, value); } @@ -455,6 +527,10 @@ void cmStateDirectory::AppendProperty(const std::string& prop, this->AppendLinkOptionsEntry(value, lfbt); return; } + if (prop == "LINK_DIRECTORIES") { + this->AppendLinkDirectoriesEntry(value, lfbt); + return; + } this->DirectoryState->Properties.AppendProperty(prop, value, asString); } @@ -542,6 +618,10 @@ const char* cmStateDirectory::GetProperty(const std::string& prop, output = cmJoin(this->GetLinkOptionsEntries(), ";"); return output.c_str(); } + if (prop == "LINK_DIRECTORIES") { + output = cmJoin(this->GetLinkDirectoriesEntries(), ";"); + return output.c_str(); + } const char* retVal = this->DirectoryState->Properties.GetPropertyValue(prop); if (!retVal && chain) { diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h index 412664f..e5f4d05 100644 --- a/Source/cmStateDirectory.h +++ b/Source/cmStateDirectory.h @@ -62,9 +62,19 @@ public: cmBacktraceRange GetLinkOptionsEntryBacktraces() const; void AppendLinkOptionsEntry(std::string const& vec, cmListFileBacktrace const& lfbt); + void PrependLinkDirectoriesEntry(std::string const& vec, + cmListFileBacktrace const& lfbt); void SetLinkOptions(std::string const& vec, cmListFileBacktrace const& lfbt); void ClearLinkOptions(); + cmStringRange GetLinkDirectoriesEntries() const; + cmBacktraceRange GetLinkDirectoriesEntryBacktraces() const; + void AppendLinkDirectoriesEntry(std::string const& vec, + cmListFileBacktrace const& lfbt); + void SetLinkDirectories(std::string const& vec, + cmListFileBacktrace const& lfbt); + void ClearLinkDirectories(); + void SetProperty(const std::string& prop, const char* value, cmListFileBacktrace const& lfbt); void AppendProperty(const std::string& prop, const char* value, diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h index 7177221..e76f2af 100644 --- a/Source/cmStatePrivate.h +++ b/Source/cmStatePrivate.h @@ -43,6 +43,7 @@ struct cmStateDetail::SnapshotDataType std::vector<std::string>::size_type CompileDefinitionsPosition; std::vector<std::string>::size_type CompileOptionsPosition; std::vector<std::string>::size_type LinkOptionsPosition; + std::vector<std::string>::size_type LinkDirectoriesPosition; }; struct cmStateDetail::PolicyStackEntry : public cmPolicies::PolicyMap @@ -88,6 +89,9 @@ struct cmStateDetail::BuildsystemDirectoryStateType std::vector<std::string> LinkOptions; std::vector<cmListFileBacktrace> LinkOptionsBacktraces; + std::vector<std::string> LinkDirectories; + std::vector<cmListFileBacktrace> LinkDirectoriesBacktraces; + std::vector<std::string> NormalTargetNames; std::string ProjectName; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index 0379e7e..c2510f3 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -398,6 +398,13 @@ void cmStateSnapshot::InitializeFromParent() this->Position->BuildSystemDirectory->LinkOptionsBacktraces, this->Position->LinkOptionsPosition); + InitializeContentFromParent( + parent->BuildSystemDirectory->LinkDirectories, + this->Position->BuildSystemDirectory->LinkDirectories, + parent->BuildSystemDirectory->LinkDirectoriesBacktraces, + this->Position->BuildSystemDirectory->LinkDirectoriesBacktraces, + this->Position->LinkDirectoriesPosition); + const char* include_regex = parent->BuildSystemDirectory->Properties.GetPropertyValue( "INCLUDE_REGULAR_EXPRESSION"); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index a338fe3..f0d6519 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -168,6 +168,8 @@ public: std::vector<cmListFileBacktrace> SourceBacktraces; std::vector<std::string> LinkOptionsEntries; std::vector<cmListFileBacktrace> LinkOptionsBacktraces; + std::vector<std::string> LinkDirectoriesEntries; + std::vector<cmListFileBacktrace> LinkDirectoriesBacktraces; std::vector<std::string> LinkImplementationPropertyEntries; std::vector<cmListFileBacktrace> LinkImplementationPropertyBacktraces; }; @@ -391,6 +393,18 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->Internal->LinkOptionsBacktraces.insert( this->Internal->LinkOptionsBacktraces.end(), parentLinkOptionsBts.begin(), parentLinkOptionsBts.end()); + + const cmStringRange parentLinkDirectories = + this->Makefile->GetLinkDirectoriesEntries(); + const cmBacktraceRange parentLinkDirectoriesBts = + this->Makefile->GetLinkDirectoriesBacktraces(); + + this->Internal->LinkDirectoriesEntries.insert( + this->Internal->LinkDirectoriesEntries.end(), + parentLinkDirectories.begin(), parentLinkDirectories.end()); + this->Internal->LinkDirectoriesBacktraces.insert( + this->Internal->LinkDirectoriesBacktraces.end(), + parentLinkDirectoriesBts.begin(), parentLinkDirectoriesBts.end()); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && @@ -539,9 +553,7 @@ void cmTarget::AddSources(std::vector<std::string> const& srcs) std::string srcFiles; const char* sep = ""; for (auto filename : srcs) { - const char* src = filename.c_str(); - - if (!(src[0] == '$' && src[1] == '<')) { + if (!cmGeneratorExpression::StartsWithGeneratorExpression(filename)) { if (!filename.empty()) { filename = this->ProcessSourceItemCMP0049(filename); if (filename.empty()) { @@ -681,19 +693,6 @@ cmSourceFile* cmTarget::AddSource(const std::string& src) cmSourceFileLocationKind::Known); } -void cmTarget::AddLinkDirectory(const std::string& d) -{ - // Make sure we don't add unnecessary search directories. - if (this->LinkDirectoriesEmmitted.insert(d).second) { - this->LinkDirectories.push_back(d); - } -} - -const std::vector<std::string>& cmTarget::GetLinkDirectories() const -{ - return this->LinkDirectories; -} - void cmTarget::ClearDependencyInformation(cmMakefile& mf) { std::string depname = this->GetName(); @@ -901,6 +900,16 @@ cmBacktraceRange cmTarget::GetLinkOptionsBacktraces() const return cmMakeRange(this->Internal->LinkOptionsBacktraces); } +cmStringRange cmTarget::GetLinkDirectoriesEntries() const +{ + return cmMakeRange(this->Internal->LinkDirectoriesEntries); +} + +cmBacktraceRange cmTarget::GetLinkDirectoriesBacktraces() const +{ + return cmMakeRange(this->Internal->LinkDirectoriesBacktraces); +} + cmStringRange cmTarget::GetLinkImplementationEntries() const { return cmMakeRange(this->Internal->LinkImplementationPropertyEntries); @@ -927,6 +936,7 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) MAKE_STATIC_PROP(IMPORTED_GLOBAL); MAKE_STATIC_PROP(INCLUDE_DIRECTORIES); MAKE_STATIC_PROP(LINK_OPTIONS); + MAKE_STATIC_PROP(LINK_DIRECTORIES); MAKE_STATIC_PROP(LINK_LIBRARIES); MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES); MAKE_STATIC_PROP(NAME); @@ -1013,6 +1023,14 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); this->Internal->LinkOptionsBacktraces.push_back(lfbt); } + } else if (prop == propLINK_DIRECTORIES) { + this->Internal->LinkDirectoriesEntries.clear(); + this->Internal->LinkDirectoriesBacktraces.clear(); + if (value) { + this->Internal->LinkDirectoriesEntries.push_back(value); + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + this->Internal->LinkDirectoriesBacktraces.push_back(lfbt); + } } else if (prop == propLINK_LIBRARIES) { this->Internal->LinkImplementationPropertyEntries.clear(); this->Internal->LinkImplementationPropertyBacktraces.clear(); @@ -1124,6 +1142,12 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value, cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); this->Internal->LinkOptionsBacktraces.push_back(lfbt); } + } else if (prop == "LINK_DIRECTORIES") { + if (value && *value) { + this->Internal->LinkDirectoriesEntries.push_back(value); + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + this->Internal->LinkDirectoriesBacktraces.push_back(lfbt); + } } else if (prop == "LINK_LIBRARIES") { if (value && *value) { cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); @@ -1221,6 +1245,21 @@ void cmTarget::InsertLinkOption(std::string const& entry, this->Internal->LinkOptionsBacktraces.insert(btPosition, bt); } +void cmTarget::InsertLinkDirectory(std::string const& entry, + cmListFileBacktrace const& bt, bool before) +{ + std::vector<std::string>::iterator position = before + ? this->Internal->LinkDirectoriesEntries.begin() + : this->Internal->LinkDirectoriesEntries.end(); + + std::vector<cmListFileBacktrace>::iterator btPosition = before + ? this->Internal->LinkDirectoriesBacktraces.begin() + : this->Internal->LinkDirectoriesBacktraces.end(); + + this->Internal->LinkDirectoriesEntries.insert(position, entry); + this->Internal->LinkDirectoriesBacktraces.insert(btPosition, bt); +} + static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop, const char* value, cmMakefile* context, @@ -1341,6 +1380,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const MAKE_STATIC_PROP(COMPILE_OPTIONS); MAKE_STATIC_PROP(COMPILE_DEFINITIONS); MAKE_STATIC_PROP(LINK_OPTIONS); + MAKE_STATIC_PROP(LINK_DIRECTORIES); MAKE_STATIC_PROP(IMPORTED); MAKE_STATIC_PROP(IMPORTED_GLOBAL); MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES); @@ -1357,6 +1397,7 @@ const char* cmTarget::GetProperty(const std::string& prop) const 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); @@ -1424,6 +1465,16 @@ const char* cmTarget::GetProperty(const std::string& prop) const output = cmJoin(this->Internal->LinkOptionsEntries, ";"); return output.c_str(); } + if (prop == propLINK_DIRECTORIES) { + if (this->Internal->LinkDirectoriesEntries.empty()) { + return nullptr; + } + + static std::string output; + output = cmJoin(this->Internal->LinkDirectoriesEntries, ";"); + + return output.c_str(); + } if (prop == propMANUALLY_ADDED_DEPENDENCIES) { if (this->Utilities.empty()) { return nullptr; diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 1f380df..694de1c 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -154,10 +154,6 @@ public: cmListFileContext const& lfc); void GetTllSignatureTraces(std::ostream& s, TLLSignature sig) const; - const std::vector<std::string>& GetLinkDirectories() const; - - void AddLinkDirectory(const std::string& d); - /** * Set the path where this target should be installed. This is relative to * INSTALL_PREFIX @@ -247,6 +243,8 @@ public: cmListFileBacktrace const& bt); void InsertLinkOption(std::string const& entry, cmListFileBacktrace const& bt, bool before = false); + void InsertLinkDirectory(std::string const& entry, + cmListFileBacktrace const& bt, bool before = false); void AppendBuildInterfaceIncludes(); @@ -277,6 +275,9 @@ public: cmStringRange GetLinkOptionsEntries() const; cmBacktraceRange GetLinkOptionsBacktraces() const; + cmStringRange GetLinkDirectoriesEntries() const; + cmBacktraceRange GetLinkDirectoriesBacktraces() const; + cmStringRange GetLinkImplementationEntries() const; cmBacktraceRange GetLinkImplementationBacktraces() const; @@ -306,14 +307,12 @@ private: bool IsGeneratorProvided; cmPropertyMap Properties; std::set<std::string> SystemIncludeDirectories; - std::set<std::string> LinkDirectoriesEmmitted; std::set<std::string> Utilities; std::map<std::string, cmListFileBacktrace> UtilityBacktraces; cmPolicies::PolicyMap PolicyMap; std::string Name; std::string InstallPath; std::string RuntimeInstallPath; - std::vector<std::string> LinkDirectories; std::vector<cmCustomCommand> PreBuildCommands; std::vector<cmCustomCommand> PreLinkCommands; std::vector<cmCustomCommand> PostBuildCommands; diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx new file mode 100644 index 0000000..bca3e45 --- /dev/null +++ b/Source/cmTargetLinkDirectoriesCommand.cxx @@ -0,0 +1,61 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmTargetLinkDirectoriesCommand.h" + +#include <sstream> + +#include "cmAlgorithms.h" +#include "cmGeneratorExpression.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmake.h" + +class cmExecutionStatus; + +bool cmTargetLinkDirectoriesCommand::InitialPass( + std::vector<std::string> const& args, cmExecutionStatus&) +{ + return this->HandleArguments(args, "LINK_DIRECTORIES", PROCESS_BEFORE); +} + +void cmTargetLinkDirectoriesCommand::HandleMissingTarget( + const std::string& name) +{ + std::ostringstream e; + e << "Cannot specify link directories for target \"" << name + << "\" which is not built by this project."; + this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); +} + +std::string cmTargetLinkDirectoriesCommand::Join( + const std::vector<std::string>& content) +{ + std::vector<std::string> directories; + + for (const auto& dir : content) { + auto unixPath = dir; + cmSystemTools::ConvertToUnixSlashes(unixPath); + if (!cmSystemTools::FileIsFullPath(unixPath) && + !cmGeneratorExpression::StartsWithGeneratorExpression(unixPath)) { + auto tmp = this->Makefile->GetCurrentSourceDirectory(); + tmp += "/"; + tmp += unixPath; + unixPath = tmp; + } + directories.push_back(unixPath); + } + + return cmJoin(directories, ";"); +} + +bool cmTargetLinkDirectoriesCommand::HandleDirectContent( + cmTarget* tgt, const std::vector<std::string>& content, bool prepend, bool) +{ + cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); + + tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend); + + return true; // Successfully handled. +} diff --git a/Source/cmTargetLinkDirectoriesCommand.h b/Source/cmTargetLinkDirectoriesCommand.h new file mode 100644 index 0000000..52c75a0 --- /dev/null +++ b/Source/cmTargetLinkDirectoriesCommand.h @@ -0,0 +1,41 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmTargetLinkDirectoriesCommand_h +#define cmTargetLinkDirectoriesCommand_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +#include "cmTargetPropCommandBase.h" + +class cmCommand; +class cmExecutionStatus; +class cmTarget; + +class cmTargetLinkDirectoriesCommand : public cmTargetPropCommandBase +{ +public: + /** + * This is a virtual constructor for the command. + */ + cmCommand* Clone() override { return new cmTargetLinkDirectoriesCommand; } + + /** + * This is called when the command is first encountered in + * the CMakeLists.txt file. + */ + bool InitialPass(std::vector<std::string> const& args, + cmExecutionStatus& status) override; + +private: + void HandleMissingTarget(const std::string& name) override; + + std::string Join(const std::vector<std::string>& content) override; + bool HandleDirectContent(cmTarget* tgt, + const std::vector<std::string>& content, + bool prepend, bool system) override; +}; + +#endif diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 6c14834..889a5fb 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -225,10 +225,8 @@ cmake::~cmake() } #if defined(CMAKE_BUILD_WITH_CMAKE) -Json::Value cmake::ReportCapabilitiesJson(bool haveServerMode) const +Json::Value cmake::ReportVersionJson() const { - Json::Value obj = Json::objectValue; - // Version information: Json::Value version = Json::objectValue; version["string"] = CMake_VERSION; version["major"] = CMake_VERSION_MAJOR; @@ -236,8 +234,15 @@ Json::Value cmake::ReportCapabilitiesJson(bool haveServerMode) const version["suffix"] = CMake_VERSION_SUFFIX; version["isDirty"] = (CMake_VERSION_IS_DIRTY == 1); version["patch"] = CMake_VERSION_PATCH; + return version; +} - obj["version"] = version; +Json::Value cmake::ReportCapabilitiesJson(bool haveServerMode) const +{ + Json::Value obj = Json::objectValue; + + // Version information: + obj["version"] = this->ReportVersionJson(); // Generators: std::vector<cmake::GeneratorInfo> generatorInfoList; @@ -2462,6 +2467,14 @@ int cmake::Build(int jobs, const std::string& dir, const std::string& target, return 1; } } + const char* cachedGeneratorPlatform = + this->State->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM"); + if (cachedGeneratorPlatform) { + cmMakefile mf(gen, this->GetCurrentSnapshot()); + if (!gen->SetGeneratorPlatform(cachedGeneratorPlatform, &mf)) { + return 1; + } + } std::string output; std::string projName; const char* cachedProjectName = diff --git a/Source/cmake.h b/Source/cmake.h index f7dc3f7..d3d0e80 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -128,6 +128,7 @@ public: ~cmake(); #if defined(CMAKE_BUILD_WITH_CMAKE) + Json::Value ReportVersionJson() const; Json::Value ReportCapabilitiesJson(bool haveServerMode) const; #endif std::string ReportCapabilities(bool haveServerMode) const; diff --git a/Tests/CMakeCommands/link_directories/CMakeLists.txt b/Tests/CMakeCommands/link_directories/CMakeLists.txt new file mode 100644 index 0000000..60c07b6 --- /dev/null +++ b/Tests/CMakeCommands/link_directories/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.12) + +project(link_directories LANGUAGES C) + + +link_directories(/A) +link_directories(BEFORE /B) + +set(CMAKE_LINK_DIRECTORIES_BEFORE ON) +link_directories(/C) + +get_directory_property(result LINK_DIRECTORIES) +if (NOT result MATCHES "/C;/B;/A") + message(SEND_ERROR "link_directories not populated the LINK_DIRECTORIES directory property") +endif() + + +add_executable(link_directories EXCLUDE_FROM_ALL LinkDirectoriesExe.c) + +get_target_property(result link_directories LINK_DIRECTORIES) +if (NOT result MATCHES "/C;/B;/A") + message(SEND_ERROR "link_directories not populated the LINK_DIRECTORIES target property") +endif() + + +add_library(imp UNKNOWN IMPORTED) +get_target_property(result imp LINK_DIRECTORIES) +if (result) + message(FATAL_ERROR "link_directories populated the LINK_DIRECTORIES target property") +endif() diff --git a/Tests/CMakeCommands/link_directories/LinkDirectoriesExe.c b/Tests/CMakeCommands/link_directories/LinkDirectoriesExe.c new file mode 100644 index 0000000..8488f4e --- /dev/null +++ b/Tests/CMakeCommands/link_directories/LinkDirectoriesExe.c @@ -0,0 +1,4 @@ +int main(void) +{ + return 0; +} diff --git a/Tests/CMakeCommands/target_link_directories/CMakeLists.txt b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt new file mode 100644 index 0000000..bc7b9b2 --- /dev/null +++ b/Tests/CMakeCommands/target_link_directories/CMakeLists.txt @@ -0,0 +1,40 @@ + +cmake_minimum_required(VERSION 3.12) + +project(target_link_directories LANGUAGES C) + +add_library(target_link_directories SHARED LinkDirectoriesLib.c) +# Test no items +target_link_directories(target_link_directories PRIVATE) + +add_library(target_link_directories_2 SHARED EXCLUDE_FROM_ALL LinkDirectoriesLib.c) +target_link_directories(target_link_directories_2 PRIVATE /private/dir INTERFACE /interface/dir) +get_target_property(result target_link_directories_2 LINK_DIRECTORIES) +if (NOT result MATCHES "/private/dir") + message(SEND_ERROR "${result} target_link_directories not populated the LINK_DIRECTORIES target property") +endif() +get_target_property(result target_link_directories_2 INTERFACE_LINK_DIRECTORIES) +if (NOT result MATCHES "/interface/dir") + message(SEND_ERROR "target_link_directories not populated the INTERFACE_LINK_DIRECTORIES target property of shared library") +endif() + +add_library(target_link_directories_3 STATIC EXCLUDE_FROM_ALL LinkDirectoriesLib.c) +target_link_directories(target_link_directories_3 INTERFACE /interface/dir) +get_target_property(result target_link_directories_3 INTERFACE_LINK_DIRECTORIES) +if (NOT result MATCHES "/interface/dir") + message(SEND_ERROR "target_link_directories not populated the INTERFACE_LINK_DIRECTORIES target property of static library") +endif() + +add_library(target_link_directories_4 SHARED EXCLUDE_FROM_ALL LinkDirectoriesLib.c) +target_link_directories(target_link_directories_4 PRIVATE relative/dir) +get_target_property(result target_link_directories_4 LINK_DIRECTORIES) +if (NOT result MATCHES "${CMAKE_CURRENT_SOURCE_DIR}/relative/dir") + message(SEND_ERROR "target_link_directories not populated the LINK_DIRECTORIES with relative path") +endif() + +add_subdirectory(subdir) +target_link_directories(target_link_directories_5 PRIVATE relative/dir) +get_target_property(result target_link_directories_5 LINK_DIRECTORIES) +if (NOT result MATCHES "${CMAKE_CURRENT_SOURCE_DIR}/relative/dir") + message(SEND_ERROR "target_link_directories not populated the LINK_DIRECTORIES with relative path") +endif() diff --git a/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c b/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c new file mode 100644 index 0000000..9bbd24c --- /dev/null +++ b/Tests/CMakeCommands/target_link_directories/LinkDirectoriesLib.c @@ -0,0 +1,7 @@ +#if defined(_WIN32) +__declspec(dllexport) +#endif + int flags_lib(void) +{ + return 0; +} diff --git a/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt b/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt new file mode 100644 index 0000000..7e7ad2a --- /dev/null +++ b/Tests/CMakeCommands/target_link_directories/subdir/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_library(target_link_directories_5 SHARED EXCLUDE_FROM_ALL ../LinkDirectoriesLib.c) diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index fb44077..0de6c41 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2128,12 +2128,13 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release get_filename_component(ntver "[HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion;CurrentVersion]" NAME) if(WIN32 AND ntver VERSION_GREATER 6.1) # Windows >= 8.0 - macro(add_test_VSWinStorePhone name generator systemName systemVersion) + macro(add_test_VSWinStorePhone name generator systemName systemVersion architecture) add_test(NAME VSWinStorePhone.${name} COMMAND ${CMAKE_CTEST_COMMAND} --build-and-test "${CMake_SOURCE_DIR}/Tests/VSWinStorePhone" "${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}" --build-generator "${generator}" + --build-generator-platform "${architecture}" --build-project VSWinStorePhone --build-config $<CONFIGURATION> --build-options -DCMAKE_SYSTEM_NAME=${systemName} @@ -2143,14 +2144,14 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release endmacro() if(vs11 AND ws80) - add_test_VSWinStorePhone(vs11-store80-X86 "Visual Studio 11 2012" WindowsStore 8.0) - add_test_VSWinStorePhone(vs11-store80-ARM "Visual Studio 11 2012 ARM" WindowsStore 8.0) - add_test_VSWinStorePhone(vs11-store80-X64 "Visual Studio 11 2012 Win64" WindowsStore 8.0) + add_test_VSWinStorePhone(vs11-store80-X86 "Visual Studio 11 2012" WindowsStore 8.0 Win32) + add_test_VSWinStorePhone(vs11-store80-ARM "Visual Studio 11 2012" WindowsStore 8.0 ARM) + add_test_VSWinStorePhone(vs11-store80-X64 "Visual Studio 11 2012" WindowsStore 8.0 x64) endif() if(vs12 AND ws81) - add_test_VSWinStorePhone(vs12-store81-X86 "Visual Studio 12 2013" WindowsStore 8.1) - add_test_VSWinStorePhone(vs12-store81-ARM "Visual Studio 12 2013 ARM" WindowsStore 8.1) - add_test_VSWinStorePhone(vs12-store81-X64 "Visual Studio 12 2013 Win64" WindowsStore 8.1) + add_test_VSWinStorePhone(vs12-store81-X86 "Visual Studio 12 2013" WindowsStore 8.1 Win32) + add_test_VSWinStorePhone(vs12-store81-ARM "Visual Studio 12 2013" WindowsStore 8.1 ARM) + add_test_VSWinStorePhone(vs12-store81-X64 "Visual Studio 12 2013" WindowsStore 8.1 x64) add_test(NAME VSXaml COMMAND ${CMAKE_CTEST_COMMAND} --build-and-test @@ -2164,22 +2165,23 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release ) endif() if(CMake_TEST_VSWinStorePhone_VS_2017 AND ws10_0) - add_test_VSWinStorePhone(vs15-store10_0-X86 "Visual Studio 15 2017" WindowsStore 10.0) - add_test_VSWinStorePhone(vs15-store10_0-ARM "Visual Studio 15 2017 ARM" WindowsStore 10.0) - add_test_VSWinStorePhone(vs15-store10_0-X64 "Visual Studio 15 2017 Win64" WindowsStore 10.0) + add_test_VSWinStorePhone(vs15-store10_0-X86 "Visual Studio 15 2017" WindowsStore 10.0 Win32) + add_test_VSWinStorePhone(vs15-store10_0-ARM "Visual Studio 15 2017" WindowsStore 10.0 ARM) + add_test_VSWinStorePhone(vs15-store10_0-X64 "Visual Studio 15 2017" WindowsStore 10.0 x64) + add_test_VSWinStorePhone(vs15-store10_0-ARM64 "Visual Studio 15 2017" WindowsStore 10.0 ARM64) endif() if(vs14 AND ws10_0) - add_test_VSWinStorePhone(vs14-store10_0-X86 "Visual Studio 14 2015" WindowsStore 10.0) - add_test_VSWinStorePhone(vs14-store10_0-ARM "Visual Studio 14 2015 ARM" WindowsStore 10.0) - add_test_VSWinStorePhone(vs14-store10_0-X64 "Visual Studio 14 2015 Win64" WindowsStore 10.0) + add_test_VSWinStorePhone(vs14-store10_0-X86 "Visual Studio 14 2015" WindowsStore 10.0 Win32) + add_test_VSWinStorePhone(vs14-store10_0-ARM "Visual Studio 14 2015" WindowsStore 10.0 ARM) + add_test_VSWinStorePhone(vs14-store10_0-X64 "Visual Studio 14 2015" WindowsStore 10.0 x64) endif() if(vs11 AND wp80) - add_test_VSWinStorePhone(vs11-phone80-X86 "Visual Studio 11 2012" WindowsPhone 8.0) - add_test_VSWinStorePhone(vs11-phone80-ARM "Visual Studio 11 2012 ARM" WindowsPhone 8.0) + add_test_VSWinStorePhone(vs11-phone80-X86 "Visual Studio 11 2012" WindowsPhone 8.0 Win32) + add_test_VSWinStorePhone(vs11-phone80-ARM "Visual Studio 11 2012" WindowsPhone 8.0 ARM) endif() if(vs12 AND wp81) - add_test_VSWinStorePhone(vs12-phone81-X86 "Visual Studio 12 2013" WindowsPhone 8.1) - add_test_VSWinStorePhone(vs12-phone81-ARM "Visual Studio 12 2013 ARM" WindowsPhone 8.1) + add_test_VSWinStorePhone(vs12-phone81-X86 "Visual Studio 12 2013" WindowsPhone 8.1 Win32) + add_test_VSWinStorePhone(vs12-phone81-ARM "Visual Studio 12 2013" WindowsPhone 8.1 ARM) endif() endif() @@ -2830,6 +2832,8 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release ADD_TEST_MACRO(CMakeCommands.add_link_options) ADD_TEST_MACRO(CMakeCommands.target_link_options) + ADD_TEST_MACRO(CMakeCommands.link_directories) + ADD_TEST_MACRO(CMakeCommands.target_link_directories) # The cmake server-mode test requires python for a simple client. find_package(PythonInterp QUIET) diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index cb048be..c6b7dbc 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -619,6 +619,18 @@ export(TARGETS testLinkOptions NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake) #------------------------------------------------------------------------------ +# test export of INTERFACE_LINK_DIRECTORIES +add_library(testLinkDirectories INTERFACE) +target_link_directories(testLinkDirectories INTERFACE + $<BUILD_INTERFACE:/interface/build> + $<INSTALL_INTERFACE:interface/install>) + +install(TARGETS testLinkDirectories + EXPORT RequiredExp DESTINATION lib) +export(TARGETS testLinkDirectories NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake) + + +#------------------------------------------------------------------------------ # test export of INTERFACE_LINK_DEPENDS if(CMAKE_GENERATOR MATCHES "Make|Ninja") add_library(testLinkDepends INTERFACE) diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt index 8791a19..67fcc02 100644 --- a/Tests/ExportImport/Import/A/CMakeLists.txt +++ b/Tests/ExportImport/Import/A/CMakeLists.txt @@ -490,6 +490,11 @@ checkForProperty(bld_testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG") checkForProperty(Req::testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG") #--------------------------------------------------------------------------------- +# check that imported libraries have the expected INTERFACE_LINK_DIRECTORIES property +checkForProperty(bld_testLinkDirectories "INTERFACE_LINK_DIRECTORIES" "/interface/build") +checkForProperty(Req::testLinkDirectories "INTERFACE_LINK_DIRECTORIES" "${CMAKE_INSTALL_PREFIX}/interface/install") + +#--------------------------------------------------------------------------------- # check that imported libraries have the expected INTERFACE_LINK_DEPENDS property if(CMAKE_GENERATOR MATCHES "Make|Ninja") checkForProperty(bld_testLinkDepends "INTERFACE_LINK_DEPENDS" "BUILD_LINK_DEPENDS") diff --git a/Tests/LinkDirectory/External/CMakeLists.txt b/Tests/LinkDirectory/External/CMakeLists.txt index f7c840f..d2a1f9f 100644 --- a/Tests/LinkDirectory/External/CMakeLists.txt +++ b/Tests/LinkDirectory/External/CMakeLists.txt @@ -1,6 +1,20 @@ cmake_minimum_required(VERSION 2.8) project(LinkDirectoryExternal C) + +add_executable(myexe2 myexe.c) +set_property(TARGET myexe2 PROPERTY OUTPUT_NAME LinkDirectory2) +target_link_directories(myexe2 PRIVATE lib "${CMAKE_CURRENT_SOURCE_DIR}/../lib") +target_link_libraries(myexe2 PRIVATE mylibA mylibB) + +add_library (mylibs INTERFACE) +target_link_directories(mylibs INTERFACE lib "${CMAKE_CURRENT_SOURCE_DIR}/../lib") +target_link_libraries(mylibs INTERFACE mylibA mylibB) +add_executable(myexe3 myexe.c) +set_property(TARGET myexe3 PROPERTY OUTPUT_NAME LinkDirectory3) +target_link_libraries(myexe3 PRIVATE mylibs) + + # Test CMP0015 OLD behavior: -L../lib cmake_policy(SET CMP0015 OLD) link_directories(../lib) diff --git a/Tests/RunCMake/CMP0081/CMP0081-Common.cmake b/Tests/RunCMake/CMP0081/CMP0081-Common.cmake new file mode 100644 index 0000000..3ea5277 --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-Common.cmake @@ -0,0 +1,5 @@ + +enable_language(CXX) + +add_library(foo SHARED empty.cpp) +set_target_properties(foo PROPERTIES LINK_DIRECTORIES "../lib") diff --git a/Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt b/Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-NEW-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt b/Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt new file mode 100644 index 0000000..d31c149 --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-NEW-stderr.txt @@ -0,0 +1,4 @@ +CMake Error in CMakeLists.txt: + Found relative path while evaluating link directories of "foo": + + "../lib" diff --git a/Tests/RunCMake/CMP0081/CMP0081-NEW.cmake b/Tests/RunCMake/CMP0081/CMP0081-NEW.cmake new file mode 100644 index 0000000..9b927a2 --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-NEW.cmake @@ -0,0 +1,4 @@ + +cmake_policy(SET CMP0081 NEW) + +include (CMP0081-Common.cmake) diff --git a/Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt b/Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-OLD-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CMP0081/CMP0081-OLD.cmake b/Tests/RunCMake/CMP0081/CMP0081-OLD.cmake new file mode 100644 index 0000000..2e91bf6 --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-OLD.cmake @@ -0,0 +1,4 @@ + +cmake_policy(SET CMP0081 OLD) + +include (CMP0081-Common.cmake) diff --git a/Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt b/Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-WARN-result.txt @@ -0,0 +1 @@ +0 diff --git a/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt b/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt new file mode 100644 index 0000000..eac0648 --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-WARN-stderr.txt @@ -0,0 +1,10 @@ +CMake Warning \(dev\) in CMakeLists.txt: + Policy CMP0081 is not set: Relative paths not allowed in LINK_DIRECTORIES + target property. Run "cmake --help-policy CMP0081" for policy details. + Use the cmake_policy command to set the policy and suppress this warning. + + Found relative path while evaluating link directories of "foo": + + "../lib" + +This warning is for project developers. Use -Wno-dev to suppress it. diff --git a/Tests/RunCMake/CMP0081/CMP0081-WARN.cmake b/Tests/RunCMake/CMP0081/CMP0081-WARN.cmake new file mode 100644 index 0000000..33bb21d --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMP0081-WARN.cmake @@ -0,0 +1,2 @@ + +include (CMP0081-Common.cmake) diff --git a/Tests/RunCMake/CMP0081/CMakeLists.txt b/Tests/RunCMake/CMP0081/CMakeLists.txt new file mode 100644 index 0000000..ef2163c --- /dev/null +++ b/Tests/RunCMake/CMP0081/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.1) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0081/RunCMakeTest.cmake b/Tests/RunCMake/CMP0081/RunCMakeTest.cmake new file mode 100644 index 0000000..335d8c5 --- /dev/null +++ b/Tests/RunCMake/CMP0081/RunCMakeTest.cmake @@ -0,0 +1,5 @@ +include(RunCMake) + +run_cmake(CMP0081-OLD) +run_cmake(CMP0081-NEW) +run_cmake(CMP0081-WARN) diff --git a/Tests/RunCMake/CMP0081/empty.cpp b/Tests/RunCMake/CMP0081/empty.cpp new file mode 100644 index 0000000..11ec041 --- /dev/null +++ b/Tests/RunCMake/CMP0081/empty.cpp @@ -0,0 +1,7 @@ +#ifdef _WIN32 +__declspec(dllexport) +#endif + int empty() +{ + return 0; +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 69cb5b7..080d0d0 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -106,6 +106,7 @@ if(CMAKE_SYSTEM_NAME MATCHES Darwin AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG) add_RunCMake_test(CMP0068) endif() add_RunCMake_test(CMP0069) +add_RunCMake_test(CMP0081) # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode diff --git a/Tests/RunCMake/CPack/DEB/Helpers.cmake b/Tests/RunCMake/CPack/DEB/Helpers.cmake index e01f81d..9b98ed4 100644 --- a/Tests/RunCMake/CPack/DEB/Helpers.cmake +++ b/Tests/RunCMake/CPack/DEB/Helpers.cmake @@ -1,4 +1,4 @@ -set(ALL_FILES_GLOB "*.deb") +set(ALL_FILES_GLOB "*.deb" "*.ddeb") function(getPackageContent FILE RESULT_VAR) execute_process(COMMAND ${CMAKE_COMMAND} -E env TZ=Etc/UTC ${DPKG_EXECUTABLE} -c "${FILE}" diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index 91fed3e..91d3cb7 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -6,7 +6,7 @@ include("${RunCMake_SOURCE_DIR}/CPackTestHelpers.cmake") # run_cpack_test args: TEST_NAME "GENERATORS" RUN_CMAKE_BUILD_STEP "PACKAGING_TYPES" run_cpack_test(CUSTOM_BINARY_SPEC_FILE "RPM" false "MONOLITHIC;COMPONENT") run_cpack_test(CUSTOM_NAMES "RPM;DEB;TGZ" true "COMPONENT") -run_cpack_test(DEBUGINFO "RPM" true "COMPONENT") +run_cpack_test(DEBUGINFO "RPM;DEB" true "COMPONENT") run_cpack_test_subtests(DEFAULT_PERMISSIONS "CMAKE_var_set;CPACK_var_set;both_set;invalid_CMAKE_var;invalid_CPACK_var" "RPM;DEB" false "MONOLITHIC;COMPONENT") run_cpack_test(DEPENDENCIES "RPM;DEB" true "COMPONENT") run_cpack_test(DIST "RPM" false "MONOLITHIC") diff --git a/Tests/RunCMake/CPack/VerifyResult.cmake b/Tests/RunCMake/CPack/VerifyResult.cmake index af12d37..345b37f 100644 --- a/Tests/RunCMake/CPack/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/VerifyResult.cmake @@ -92,7 +92,7 @@ if(NOT EXPECTED_FILES_COUNT EQUAL 0) # check that there were no extra files generated foreach(all_files_glob_ IN LISTS ALL_FILES_GLOB) file(GLOB foundAll_ RELATIVE "${bin_dir}" "${all_files_glob_}") - list(APPEND allFoundFiles_ "${foundAll_}") + list(APPEND allFoundFiles_ ${foundAll_}) endforeach() list(LENGTH foundFiles_ foundFilesCount_) diff --git a/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake index c745828..cf2e8ac 100644 --- a/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake +++ b/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake @@ -3,16 +3,39 @@ set(whitespaces_ "[\t\n\r ]*") set(EXPECTED_FILES_COUNT "5") set(EXPECTED_FILES_NAME_GENERATOR_SPECIFIC_FORMAT TRUE) -set(EXPECTED_FILE_1_NAME "Debuginfo") +if(GENERATOR_TYPE STREQUAL "RPM") + set(NAME "Debuginfo") + set(DEBUG_SUFFIX "debuginfo") + set(PKG "rpm") + set(DEBUG_PKG "rpm") +elseif(GENERATOR_TYPE STREQUAL "DEB") + set(NAME "debuginfo") + set(DEBUG_SUFFIX "dbgsym") + set(PKG "deb") + set(DEBUG_PKG "ddeb") +endif() + +set(EXPECTED_FILE_1_NAME "${NAME}") set(EXPECTED_FILE_1_COMPONENT "applications") set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/test_prog") -set(EXPECTED_FILE_2 "TestDinfo-pkg*-headers.rpm") + +set(EXPECTED_FILE_2 "TestDinfo-pkg*-headers.${PKG}") set(EXPECTED_FILE_CONTENT_2_LIST "/bar;/bar/CMakeLists.txt") -set(EXPECTED_FILE_3 "TestDinfo-pkg*-libs.rpm") + +set(EXPECTED_FILE_3 "TestDinfo-pkg*-libs.${PKG}") set(EXPECTED_FILE_CONTENT_3_LIST "/bas;/bas/libtest_lib.so") -set(EXPECTED_FILE_4_NAME "Debuginfo") -set(EXPECTED_FILE_4_COMPONENT "applications-debuginfo") -set(EXPECTED_FILE_CONTENT_4 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/main.cpp.*\.debug.*") -set(EXPECTED_FILE_5 "libs-DebugInfoPackage.rpm") -set(EXPECTED_FILE_CONTENT_5 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/test_lib.cpp.*\.debug.*") +set(EXPECTED_FILE_4 "${NAME}-applications-${DEBUG_SUFFIX}*.${DEBUG_PKG}") +if(GENERATOR_TYPE STREQUAL "RPM") + set(EXPECTED_FILE_CONTENT_4 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/main.cpp.*\.debug.*") +elseif(GENERATOR_TYPE STREQUAL "DEB") + set(EXPECTED_FILE_CONTENT_4 ".*/usr/lib/debug/.build-id/.*\.debug.*") +endif() + +if(GENERATOR_TYPE STREQUAL "RPM") + set(EXPECTED_FILE_5 "libs-DebugInfoPackage.rpm") + set(EXPECTED_FILE_CONTENT_5 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/test_lib.cpp.*\.debug.*") +elseif(GENERATOR_TYPE STREQUAL "DEB") + set(EXPECTED_FILE_5 "TestDinfo-pkg-libs-dbgsym.ddeb") + set(EXPECTED_FILE_CONTENT_5 ".*/usr/lib/debug/.build-id/.*\.debug.*") +endif() diff --git a/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake b/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake index 71457d4..161a36a 100644 --- a/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake +++ b/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake @@ -29,12 +29,16 @@ install(TARGETS test_lib DESTINATION bas COMPONENT libs) set(CPACK_RPM_APPLICATIONS_FILE_NAME "RPM-DEFAULT") set(CPACK_RPM_APPLICATIONS_DEBUGINFO_PACKAGE ON) +set(CPACK_DEBIAN_APPLICATIONS_FILE_NAME "DEB-DEFAULT") +set(CPACK_DEBIAN_APPLICATIONS_DEBUGINFO_PACKAGE ON) # test that components with debuginfo enabled still honor # CPACK_PACKAGE_FILE_NAME setting set(CPACK_RPM_PACKAGE_NAME "Debuginfo") set(CPACK_PACKAGE_FILE_NAME "TestDinfo-pkg") set(CPACK_RPM_LIBS_DEBUGINFO_PACKAGE ON) +set(CPACK_DEBIAN_PACKAGE_NAME "Debuginfo") +set(CPACK_DEBIAN_LIBS_DEBUGINFO_PACKAGE ON) # test debuginfo package rename set(CPACK_RPM_DEBUGINFO_FILE_NAME diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt index 6c861fa..2441a9c 100644 --- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt +++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt @@ -25,6 +25,7 @@ \* CMP0069 \* CMP0073 \* CMP0076 + \* CMP0081 Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt b/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt new file mode 100644 index 0000000..580c373 --- /dev/null +++ b/Tests/RunCMake/set_property/LINK_DIRECTORIES-stdout.txt @@ -0,0 +1,2 @@ +-- Target LINK_DIRECTORIES is 'a;b;c;d;;e' +-- Directory LINK_DIRECTORIES is 'a;b;c;d;;e' diff --git a/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake b/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake new file mode 100644 index 0000000..8529ef5 --- /dev/null +++ b/Tests/RunCMake/set_property/LINK_DIRECTORIES.cmake @@ -0,0 +1,3 @@ +include(Common.cmake) +test_target_property(LINK_DIRECTORIES) +test_directory_property(LINK_DIRECTORIES) diff --git a/Tests/RunCMake/set_property/RunCMakeTest.cmake b/Tests/RunCMake/set_property/RunCMakeTest.cmake index 77da703..8d4614c 100644 --- a/Tests/RunCMake/set_property/RunCMakeTest.cmake +++ b/Tests/RunCMake/set_property/RunCMakeTest.cmake @@ -6,6 +6,7 @@ run_cmake(COMPILE_OPTIONS) run_cmake(IMPORTED_GLOBAL) run_cmake(INCLUDE_DIRECTORIES) run_cmake(LINK_OPTIONS) +run_cmake(LINK_DIRECTORIES) run_cmake(LINK_LIBRARIES) run_cmake(SOURCES) run_cmake(TYPE) diff --git a/Utilities/Release/linux64_release.cmake b/Utilities/Release/linux64_release.cmake index f2ca2d5..dc34120 100644 --- a/Utilities/Release/linux64_release.cmake +++ b/Utilities/Release/linux64_release.cmake @@ -3,8 +3,8 @@ set(BOOTSTRAP_ARGS "--docdir=doc/cmake") set(HOST linux64) set(MAKE_PROGRAM "make") set(CPACK_BINARY_GENERATORS "STGZ TGZ") -set(CC /opt/gcc-6.1.0/bin/gcc) -set(CXX /opt/gcc-6.1.0/bin/g++) +set(CC /opt/gcc-8.2.0/bin/gcc) +set(CXX /opt/gcc-8.2.0/bin/g++) set(CFLAGS "") set(CXXFLAGS "") set(qt_prefix "/home/kitware/qt-5.7.0") @@ -41,6 +41,9 @@ CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL:STRING=3 CMAKE_PREFIX_PATH:STRING=${qt_prefix} CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES:STRING=${qt_xcb_libs} ") +set(ENV [[ +export CMAKE_PREFIX_PATH=/opt/binutils-2.31 +]]) # Exclude Qt5 tests because our Qt5 is static. set(EXTRA_CTEST_ARGS "-E Qt5") diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash index 7534f94..3188658 100755 --- a/Utilities/Scripts/update-libarchive.bash +++ b/Utilities/Scripts/update-libarchive.bash @@ -8,7 +8,7 @@ readonly name="LibArchive" readonly ownership="LibArchive Upstream <libarchive-discuss@googlegroups.com>" readonly subtree="Utilities/cmlibarchive" readonly repo="https://github.com/libarchive/libarchive.git" -readonly tag="v3.3.2" +readonly tag="v3.3.3" readonly shortlog=false readonly paths=" CMakeLists.txt diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt index 206f3c6..d7af6e2 100644 --- a/Utilities/cmlibarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/CMakeLists.txt @@ -71,6 +71,7 @@ include(CTest) OPTION(ENABLE_NETTLE "Enable use of Nettle" ON) OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON) +OPTION(ENABLE_LZ4 "Enable the use of the system LZ4 library if found" ON) OPTION(ENABLE_LZO "Enable the use of the system LZO library if found" OFF) OPTION(ENABLE_LZMA "Enable the use of the system LZMA library if found" ON) @@ -328,7 +329,7 @@ IF(ENABLE_LZO) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZO2 DEFAULT_MSG LZO2_LIBRARY LZO2_INCLUDE_DIR) ELSE(ENABLE_LZO) - SET(LIBZMA_FOUND FALSE) # Override cached value + SET(LZO2_FOUND FALSE) # Override cached value ENDIF(ENABLE_LZO) IF(LZO2_FOUND) SET(HAVE_LIBLZO2 1) @@ -347,15 +348,19 @@ IF(0) # CMake does not need LZ4 support in libarchive # # Find LZ4 # -IF (LZ4_INCLUDE_DIR) - # Already in cache, be silent - SET(LZ4_FIND_QUIETLY TRUE) -ENDIF (LZ4_INCLUDE_DIR) +IF(ENABLE_LZ4) + IF (LZ4_INCLUDE_DIR) + # Already in cache, be silent + SET(LZ4_FIND_QUIETLY TRUE) + ENDIF (LZ4_INCLUDE_DIR) -FIND_PATH(LZ4_INCLUDE_DIR lz4.h) -FIND_LIBRARY(LZ4_LIBRARY NAMES lz4 liblz4) -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR) + FIND_PATH(LZ4_INCLUDE_DIR lz4.h) + FIND_LIBRARY(LZ4_LIBRARY NAMES lz4 liblz4) + INCLUDE(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR) +ELSE(ENABLE_LZ4) + SET(LZ4_FOUND FALSE) # Override cached value +ENDIF(ENABLE_LZ4) IF(LZ4_FOUND) SET(HAVE_LIBLZ4 1) SET(HAVE_LZ4_H 1) @@ -371,6 +376,31 @@ IF(LZ4_FOUND) ENDIF(LZ4_FOUND) MARK_AS_ADVANCED(CLEAR LZ4_INCLUDE_DIR) MARK_AS_ADVANCED(CLEAR LZ4_LIBRARY) +# +# Find Zstd +# +IF (ZSTD_INCLUDE_DIR) + # Already in cache, be silent + SET(ZSTD_FIND_QUIETLY TRUE) +ENDIF (ZSTD_INCLUDE_DIR) + +FIND_PATH(ZSTD_INCLUDE_DIR zstd.h) +FIND_LIBRARY(ZSTD_LIBRARY NAMES zstd libzstd) +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) +IF(ZSTD_FOUND) + SET(HAVE_ZSTD_H 1) + INCLUDE_DIRECTORIES(${ZSTD_INCLUDE_DIR}) + LIST(APPEND ADDITIONAL_LIBS ${ZSTD_LIBRARY}) + SET(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY}) + SET(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR}) + CHECK_FUNCTION_EXISTS(ZSTD_compressStream HAVE_LIBZSTD) + # + # TODO: test for static library. + # +ENDIF(ZSTD_FOUND) +MARK_AS_ADVANCED(CLEAR ZSTD_INCLUDE_DIR) +MARK_AS_ADVANCED(CLEAR ZSTD_LIBRARY) ENDIF() # @@ -451,6 +481,7 @@ LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H) LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H) LA_CHECK_INCLUDE_FILE("sys/statfs.h" HAVE_SYS_STATFS_H) LA_CHECK_INCLUDE_FILE("sys/statvfs.h" HAVE_SYS_STATVFS_H) +LA_CHECK_INCLUDE_FILE("sys/sysmacros.h" HAVE_SYS_SYSMACROS_H) LA_CHECK_INCLUDE_FILE("sys/time.h" HAVE_SYS_TIME_H) LA_CHECK_INCLUDE_FILE("sys/utime.h" HAVE_SYS_UTIME_H) LA_CHECK_INCLUDE_FILE("sys/utsname.h" HAVE_SYS_UTSNAME_H) diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in index 368a451..1851d81 100644 --- a/Utilities/cmlibarchive/build/cmake/config.h.in +++ b/Utilities/cmlibarchive/build/cmake/config.h.in @@ -728,6 +728,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the `z' library (-lz). */ #cmakedefine HAVE_LIBZ 1 +/* Define to 1 if you have the `zstd' library (-lzstd). */ +#cmakedefine HAVE_LIBZSTD 1 + /* Define to 1 if you have the <limits.h> header file. */ #cmakedefine HAVE_LIMITS_H 1 @@ -1071,6 +1074,10 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the <sys/stat.h> header file. */ #cmakedefine HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/sysmacros.h> header file. */ +#cmakedefine HAVE_SYS_SYSMACROS_H 1 + /* Define to 1 if you have the <sys/time.h> header file. */ #cmakedefine HAVE_SYS_TIME_H 1 @@ -1185,6 +1192,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the <zlib.h> header file. */ #cmakedefine HAVE_ZLIB_H 1 +/* Define to 1 if you have the <zstd.h> header file. */ +#cmakedefine HAVE_ZSTD_H 1 + /* Define to 1 if you have the `_ctime64_s' function. */ #cmakedefine HAVE__CTIME64_S 1 diff --git a/Utilities/cmlibarchive/build/pkgconfig/libarchive.pc.in b/Utilities/cmlibarchive/build/pkgconfig/libarchive.pc.in index 95d7159..4b631e6 100644 --- a/Utilities/cmlibarchive/build/pkgconfig/libarchive.pc.in +++ b/Utilities/cmlibarchive/build/pkgconfig/libarchive.pc.in @@ -7,5 +7,6 @@ Name: libarchive Description: library that can create and read several streaming archive formats Version: @VERSION@ Cflags: -I${includedir} +Cflags.private: -DLIBARCHIVE_STATIC Libs: -L${libdir} -larchive Libs.private: @LIBS@ diff --git a/Utilities/cmlibarchive/build/version b/Utilities/cmlibarchive/build/version index 2dd0839..2427eab 100644 --- a/Utilities/cmlibarchive/build/version +++ b/Utilities/cmlibarchive/build/version @@ -1 +1 @@ -3003002 +3003003 diff --git a/Utilities/cmlibarchive/libarchive/CMakeLists.txt b/Utilities/cmlibarchive/libarchive/CMakeLists.txt index b02ae82..e38d664 100644 --- a/Utilities/cmlibarchive/libarchive/CMakeLists.txt +++ b/Utilities/cmlibarchive/libarchive/CMakeLists.txt @@ -88,6 +88,7 @@ SET(libarchive_SOURCES archive_read_support_filter_rpm.c archive_read_support_filter_uu.c archive_read_support_filter_xz.c + archive_read_support_filter_zstd.c archive_read_support_format_7zip.c archive_read_support_format_all.c archive_read_support_format_ar.c @@ -134,6 +135,7 @@ SET(libarchive_SOURCES archive_write_add_filter_program.c archive_write_add_filter_uuencode.c archive_write_add_filter_xz.c + archive_write_add_filter_zstd.c archive_write_set_format.c archive_write_set_format_7zip.c archive_write_set_format_ar.c diff --git a/Utilities/cmlibarchive/libarchive/archive.h b/Utilities/cmlibarchive/libarchive/archive.h index 0f94d2e..f3ebbfe 100644 --- a/Utilities/cmlibarchive/libarchive/archive.h +++ b/Utilities/cmlibarchive/libarchive/archive.h @@ -36,7 +36,7 @@ * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3003002 +#define ARCHIVE_VERSION_NUMBER 3003003 #include <sys/stat.h> #include <stddef.h> /* for wchar_t */ @@ -152,7 +152,7 @@ __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_ONLY_STRING "3.3.2" +#define ARCHIVE_VERSION_ONLY_STRING "3.3.3" #define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING __LA_DECL const char * archive_version_string(void); @@ -174,6 +174,7 @@ __LA_DECL const char * archive_zlib_version(void); __LA_DECL const char * archive_liblzma_version(void); __LA_DECL const char * archive_bzlib_version(void); __LA_DECL const char * archive_liblz4_version(void); +__LA_DECL const char * archive_libzstd_version(void); /* Declare our basic types. */ struct archive; @@ -273,6 +274,7 @@ typedef const char *archive_passphrase_callback(struct archive *, #define ARCHIVE_FILTER_LZOP 11 #define ARCHIVE_FILTER_GRZIP 12 #define ARCHIVE_FILTER_LZ4 13 +#define ARCHIVE_FILTER_ZSTD 14 #if ARCHIVE_VERSION_NUMBER < 4000000 #define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE @@ -430,6 +432,7 @@ __LA_DECL int archive_read_support_filter_program_signature __LA_DECL int archive_read_support_filter_rpm(struct archive *); __LA_DECL int archive_read_support_filter_uu(struct archive *); __LA_DECL int archive_read_support_filter_xz(struct archive *); +__LA_DECL int archive_read_support_filter_zstd(struct archive *); __LA_DECL int archive_read_support_format_7zip(struct archive *); __LA_DECL int archive_read_support_format_all(struct archive *); @@ -775,6 +778,7 @@ __LA_DECL int archive_write_add_filter_program(struct archive *, const char *cmd); __LA_DECL int archive_write_add_filter_uuencode(struct archive *); __LA_DECL int archive_write_add_filter_xz(struct archive *); +__LA_DECL int archive_write_add_filter_zstd(struct archive *); /* A convenience function to set the format based on the code or name. */ diff --git a/Utilities/cmlibarchive/libarchive/archive_acl.c b/Utilities/cmlibarchive/libarchive/archive_acl.c index b8b6b63..4736531 100644 --- a/Utilities/cmlibarchive/libarchive/archive_acl.c +++ b/Utilities/cmlibarchive/libarchive/archive_acl.c @@ -1159,6 +1159,7 @@ archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text, switch (want_type) { case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + __LA_FALLTHROUGH; case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: numfields = 5; @@ -1626,6 +1627,7 @@ archive_acl_from_text_l(struct archive_acl *acl, const char *text, switch (want_type) { case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; + __LA_FALLTHROUGH; case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: numfields = 5; diff --git a/Utilities/cmlibarchive/libarchive/archive_cmdline.c b/Utilities/cmlibarchive/libarchive/archive_cmdline.c index 7d3bac5..5c519cd 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cmdline.c +++ b/Utilities/cmlibarchive/libarchive/archive_cmdline.c @@ -100,10 +100,10 @@ get_argument(struct archive_string *as, const char *p) /* * Set up command line arguments. - * Returns ARChIVE_OK if everything okey. - * Returns ARChIVE_FAILED if there is a lack of the `"' terminator or an + * Returns ARCHIVE_OK if everything okey. + * Returns ARCHIVE_FAILED if there is a lack of the `"' terminator or an * empty command line. - * Returns ARChIVE_FATAL if no memory. + * Returns ARCHIVE_FATAL if no memory. */ int __archive_cmdline_parse(struct archive_cmdline *data, const char *cmd) diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor.c b/Utilities/cmlibarchive/libarchive/archive_cryptor.c index ced52fd..71967c9 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cryptor.c +++ b/Utilities/cmlibarchive/libarchive/archive_cryptor.c @@ -153,7 +153,7 @@ aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) CCCryptorStatus r; r = CCCryptorReset(ref, NULL); - if (r != kCCSuccess) + if (r != kCCSuccess && r != kCCUnimplemented) return -1; r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf, AES_BLOCK_SIZE, NULL); diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h index 0ca544b..b975922 100644 --- a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h @@ -64,7 +64,7 @@ typedef struct { } archive_crypto_ctx; #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) -#include <Bcrypt.h> +#include <bcrypt.h> /* Common in other bcrypt implementations, but missing from VS2008. */ #ifndef BCRYPT_SUCCESS diff --git a/Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c b/Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c index 07d08ff..aba41e5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c +++ b/Utilities/cmlibarchive/libarchive/archive_disk_acl_freebsd.c @@ -93,7 +93,9 @@ static const acl_perm_map_t acl_nfs4_flag_map[] = { {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}, {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, +#ifdef ACL_ENTRY_INHERITED {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED} +#endif }; static const int acl_nfs4_flag_map_size = diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.c b/Utilities/cmlibarchive/libarchive/archive_entry.c index 30fb456..f722bbe 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.c +++ b/Utilities/cmlibarchive/libarchive/archive_entry.c @@ -1491,7 +1491,7 @@ archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, * the style of the generated ACL. */ wchar_t * -archive_entry_acl_to_text_w(struct archive_entry *entry, ssize_t *len, +archive_entry_acl_to_text_w(struct archive_entry *entry, la_ssize_t *len, int flags) { return (archive_acl_to_text_w(&entry->acl, len, flags, @@ -1499,7 +1499,7 @@ archive_entry_acl_to_text_w(struct archive_entry *entry, ssize_t *len, } char * -archive_entry_acl_to_text(struct archive_entry *entry, ssize_t *len, +archive_entry_acl_to_text(struct archive_entry *entry, la_ssize_t *len, int flags) { return (archive_acl_to_text_l(&entry->acl, len, flags, NULL)); diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h index 8c8e75a..a9134f6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_entry.h +++ b/Utilities/cmlibarchive/libarchive/archive_entry.h @@ -30,7 +30,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3003002 +#define ARCHIVE_VERSION_NUMBER 3003003 /* * Note: archive_entry.h is for use outside of libarchive; the @@ -42,6 +42,7 @@ #include <sys/types.h> #include <stddef.h> /* for wchar_t */ +#include <stdint.h> #include <time.h> #if defined(_WIN32) && !defined(__CYGWIN__) diff --git a/Utilities/cmlibarchive/libarchive/archive_match.c b/Utilities/cmlibarchive/libarchive/archive_match.c index be72066..f150e82 100644 --- a/Utilities/cmlibarchive/libarchive/archive_match.c +++ b/Utilities/cmlibarchive/libarchive/archive_match.c @@ -1582,7 +1582,7 @@ time_excluded(struct archive_match *a, struct archive_entry *entry) */ int -archive_match_include_uid(struct archive *_a, int64_t uid) +archive_match_include_uid(struct archive *_a, la_int64_t uid) { struct archive_match *a; @@ -1593,7 +1593,7 @@ archive_match_include_uid(struct archive *_a, int64_t uid) } int -archive_match_include_gid(struct archive *_a, int64_t gid) +archive_match_include_gid(struct archive *_a, la_int64_t gid) { struct archive_match *a; diff --git a/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h b/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h index 59f95b8..921249b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_openssl_hmac_private.h @@ -28,7 +28,8 @@ #include <openssl/hmac.h> #include <openssl/opensslv.h> -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) #include <stdlib.h> /* malloc, free */ #include <string.h> /* memset */ static inline HMAC_CTX *HMAC_CTX_new(void) diff --git a/Utilities/cmlibarchive/libarchive/archive_pack_dev.c b/Utilities/cmlibarchive/libarchive/archive_pack_dev.c index 098881b..53bddd7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_pack_dev.c +++ b/Utilities/cmlibarchive/libarchive/archive_pack_dev.c @@ -57,6 +57,9 @@ __RCSID("$NetBSD$"); #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> #endif +#ifdef HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_platform.h b/Utilities/cmlibarchive/libarchive/archive_platform.h index f33208c..e161e64 100644 --- a/Utilities/cmlibarchive/libarchive/archive_platform.h +++ b/Utilities/cmlibarchive/libarchive/archive_platform.h @@ -196,4 +196,10 @@ #define ARCHIVE_ERRNO_MISC (-1) #endif +#if defined(__GNUC__) && (__GNUC__ >= 7) +#define __LA_FALLTHROUGH __attribute__((fallthrough)) +#else +#define __LA_FALLTHROUGH +#endif + #endif /* !ARCHIVE_PLATFORM_H_INCLUDED */ diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd7.c b/Utilities/cmlibarchive/libarchive/archive_ppmd7.c index 1aed922..d0bacc6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd7.c +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd7.c @@ -115,14 +115,14 @@ static void Ppmd7_Construct(CPpmd7 *p) memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40); } -static void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc) +static void Ppmd7_Free(CPpmd7 *p) { - alloc->Free(alloc, p->Base); + free(p->Base); p->Size = 0; p->Base = 0; } -static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc) +static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size) { if (p->Base == 0 || p->Size != size) { @@ -131,14 +131,14 @@ static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc) if (size < UNIT_SIZE) { return False; } - Ppmd7_Free(p, alloc); + Ppmd7_Free(p); p->AlignOffset = #ifdef PPMD_32BIT (4 - size) & 3; #else 4 - (size & 3); #endif - if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size + if ((p->Base = (Byte *)malloc(p->AlignOffset + size #ifndef PPMD_32BIT + UNIT_SIZE #endif diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h b/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h index 06c99e8..577d6fb 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd7_private.h @@ -95,8 +95,8 @@ typedef struct { /* Base Functions */ void (*Ppmd7_Construct)(CPpmd7 *p); - Bool (*Ppmd7_Alloc)(CPpmd7 *p, UInt32 size, ISzAlloc *alloc); - void (*Ppmd7_Free)(CPpmd7 *p, ISzAlloc *alloc); + Bool (*Ppmd7_Alloc)(CPpmd7 *p, UInt32 size); + void (*Ppmd7_Free)(CPpmd7 *p); void (*Ppmd7_Init)(CPpmd7 *p, unsigned maxOrder); #define Ppmd7_WasAllocated(p) ((p)->Base != NULL) diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h b/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h index e78bde5..a83b851 100644 --- a/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h +++ b/Utilities/cmlibarchive/libarchive/archive_ppmd_private.h @@ -69,13 +69,6 @@ typedef struct void (*Write)(void *p, Byte b); } IByteOut; - -typedef struct -{ - void *(*Alloc)(void *p, size_t size); - void (*Free)(void *p, void *address); /* address can be 0 */ -} ISzAlloc; - /*** End defined in Types.h ***/ /*** Begin defined in CpuArch.h ***/ diff --git a/Utilities/cmlibarchive/libarchive/archive_read.c b/Utilities/cmlibarchive/libarchive/archive_read.c index a642a33..0e56e76 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read.c +++ b/Utilities/cmlibarchive/libarchive/archive_read.c @@ -120,7 +120,8 @@ archive_read_new(void) * Record the do-not-extract-to file. This belongs in archive_read_extract.c. */ void -archive_read_extract_set_skip_file(struct archive *_a, int64_t d, int64_t i) +archive_read_extract_set_skip_file(struct archive *_a, la_int64_t d, + la_int64_t i) { struct archive_read *a = (struct archive_read *)_a; @@ -747,7 +748,7 @@ choose_format(struct archive_read *a) * Return the file offset (within the uncompressed data stream) where * the last header started. */ -int64_t +la_int64_t archive_read_header_position(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; @@ -820,7 +821,7 @@ archive_read_format_capabilities(struct archive *_a) * DO NOT intermingle calls to this function and archive_read_data_block * to read a single entry body. */ -ssize_t +la_ssize_t archive_read_data(struct archive *_a, void *buff, size_t s) { struct archive *a = (struct archive *)_a; @@ -943,7 +944,7 @@ archive_read_data_skip(struct archive *_a) return (r); } -int64_t +la_int64_t archive_seek_data(struct archive *_a, int64_t offset, int whence) { struct archive_read *a = (struct archive_read *)_a; @@ -1626,7 +1627,8 @@ __archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, switch (whence) { case SEEK_CUR: /* Adjust the offset and use SEEK_SET instead */ - offset += filter->position; + offset += filter->position; + __LA_FALLTHROUGH; case SEEK_SET: cursor = 0; while (1) diff --git a/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c b/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c index 5e4d163..da7c55b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_append_filter.c @@ -89,6 +89,10 @@ archive_read_append_filter(struct archive *_a, int code) strcpy(str, "lz4"); r1 = archive_read_support_filter_lz4(_a); break; + case ARCHIVE_FILTER_ZSTD: + strcpy(str, "zstd"); + r1 = archive_read_support_filter_zstd(_a); + break; case ARCHIVE_FILTER_LZIP: strcpy(str, "lzip"); r1 = archive_read_support_filter_lzip(_a); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c index 548ba89..1786cff 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c @@ -127,7 +127,7 @@ archive_read_disk_entry_setup_acls(struct archive_read_disk *a, /* * Enter working directory and return working pathname of archive_entry. * If a pointer to an integer is provided and its value is below zero - * open a file descriptor on this pahtname. + * open a file descriptor on this pathname. */ const char * archive_read_disk_entry_setup_path(struct archive_read_disk *a, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c index 6961ae6..cdf7541 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c @@ -387,7 +387,7 @@ archive_read_disk_vtable(void) } const char * -archive_read_disk_gname(struct archive *_a, int64_t gid) +archive_read_disk_gname(struct archive *_a, la_int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, @@ -399,7 +399,7 @@ archive_read_disk_gname(struct archive *_a, int64_t gid) } const char * -archive_read_disk_uname(struct archive *_a, int64_t uid) +archive_read_disk_uname(struct archive *_a, la_int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, @@ -413,7 +413,7 @@ archive_read_disk_uname(struct archive *_a, int64_t uid) int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, - const char * (*lookup_gname)(void *private, int64_t gid), + const char * (*lookup_gname)(void *private, la_int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; @@ -432,7 +432,7 @@ archive_read_disk_set_gname_lookup(struct archive *_a, int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, - const char * (*lookup_uname)(void *private, int64_t uid), + const char * (*lookup_uname)(void *private, la_int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c index 3b90330..d82048d 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c @@ -117,7 +117,7 @@ struct filesystem { */ #define MAX_OVERLAPPED 8 -#define BUFFER_SIZE (1024 * 8) +#define READ_BUFFER_SIZE (1024 * 64) /* Default to 64KB per https://technet.microsoft.com/en-us/library/cc938632.aspx */ #define DIRECT_IO 0/* Disabled */ #define ASYNC_IO 1/* Enabled */ @@ -320,7 +320,7 @@ archive_read_disk_vtable(void) } const char * -archive_read_disk_gname(struct archive *_a, int64_t gid) +archive_read_disk_gname(struct archive *_a, la_int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, @@ -332,7 +332,7 @@ archive_read_disk_gname(struct archive *_a, int64_t gid) } const char * -archive_read_disk_uname(struct archive *_a, int64_t uid) +archive_read_disk_uname(struct archive *_a, la_int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, @@ -346,7 +346,7 @@ archive_read_disk_uname(struct archive *_a, int64_t uid) int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, - const char * (*lookup_gname)(void *private, int64_t gid), + const char * (*lookup_gname)(void *private, la_int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; @@ -567,7 +567,7 @@ start_next_async_read(struct archive_read_disk *a, struct tree *t) /* Allocate read buffer. */ if (olp->buff == NULL) { void *p; - size_t s = (size_t)align_num_per_sector(t, BUFFER_SIZE); + size_t s = (size_t)align_num_per_sector(t, READ_BUFFER_SIZE); p = VirtualAlloc(NULL, s, MEM_COMMIT, PAGE_READWRITE); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -683,7 +683,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, break; } while (r == ARCHIVE_OK && t->ol_num_doing < MAX_OVERLAPPED); } else { - if (start_next_async_read(a, t) == ARCHIVE_FATAL) + if ((r = start_next_async_read(a, t)) == ARCHIVE_FATAL) goto abort_read_data; } @@ -923,6 +923,7 @@ next_entry(struct archive_read_disk *a, struct tree *t, t->entry_fh = CreateFileW(tree_current_access_path(t), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL); if (t->entry_fh == INVALID_HANDLE_VALUE) { + la_dosmaperr(GetLastError()); archive_set_error(&a->archive, errno, "Couldn't open %ls", tree_current_path(a->tree)); return (ARCHIVE_FAILED); @@ -2275,10 +2276,10 @@ setup_sparse_from_disk(struct archive_read_disk *a, if (range.Length.QuadPart > 0) continue; } else { - /* The remaining data is hole. */ + /* The entire file is a hole. Add one data block of size 0 at the end. */ archive_entry_sparse_add_entry(entry, - range.FileOffset.QuadPart, - range.Length.QuadPart); + entry_size, + 0); } break; } else { diff --git a/Utilities/cmlibarchive/libarchive/archive_read_filter.3 b/Utilities/cmlibarchive/libarchive/archive_read_filter.3 index 7f020e3..ef0a701 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_filter.3 +++ b/Utilities/cmlibarchive/libarchive/archive_read_filter.3 @@ -38,6 +38,7 @@ .Nm archive_read_support_filter_rpm , .Nm archive_read_support_filter_uu , .Nm archive_read_support_filter_xz , +.Nm archive_read_support_filter_zstd , .Nm archive_read_support_filter_program , .Nm archive_read_support_filter_program_signature .Nd functions for reading streaming archives @@ -73,6 +74,8 @@ Streaming Archive Library (libarchive, -larchive) .Ft int .Fn archive_read_support_filter_xz "struct archive *" .Ft int +.Fn archive_read_support_filter_zstd "struct archive *" +.Ft int .Fo archive_read_support_filter_program .Fa "struct archive *" .Fa "const char *cmd" @@ -99,7 +102,8 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_read_support_filter_none , .Fn archive_read_support_filter_rpm , .Fn archive_read_support_filter_uu , -.Fn archive_read_support_filter_xz +.Fn archive_read_support_filter_xz , +.Fn archive_read_support_filter_zstd , .Xc Enables auto-detection code and decompression support for the specified compression. diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c index 68c53de..edb508c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_all.c @@ -71,6 +71,8 @@ archive_read_support_filter_all(struct archive *a) archive_read_support_filter_grzip(a); /* Lz4 falls back to "lz4 -d" command-line program. */ archive_read_support_filter_lz4(a); + /* Zstd falls back to "zstd -d" command-line program. */ + archive_read_support_filter_zstd(a); /* Note: We always return ARCHIVE_OK here, even if some of the * above return ARCHIVE_WARN. The intent here is to enable diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c new file mode 100644 index 0000000..c8bb36b --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 2009-2011 Sean Purcell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" + +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_ZSTD_H +#include <zstd.h> +#endif + +#include "archive.h" +#include "archive_endian.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#if HAVE_ZSTD_H && HAVE_LIBZSTD + +struct private_data { + ZSTD_DStream *dstream; + unsigned char *out_block; + size_t out_block_size; + int64_t total_out; + char in_frame; /* True = in the middle of a zstd frame. */ + char eof; /* True = found end of compressed data. */ +}; + +/* Zstd Filter. */ +static ssize_t zstd_filter_read(struct archive_read_filter *, const void**); +static int zstd_filter_close(struct archive_read_filter *); +#endif + +/* + * Note that we can detect zstd compressed files even if we can't decompress + * them. (In fact, we like detecting them because we can give better error + * messages.) So the bid framework here gets compiled even if no zstd library + * is available. + */ +static int zstd_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *); +static int zstd_bidder_init(struct archive_read_filter *); + +int +archive_read_support_filter_zstd(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *bidder; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_filter_zstd"); + + if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + bidder->data = NULL; + bidder->name = "zstd"; + bidder->bid = zstd_bidder_bid; + bidder->init = zstd_bidder_init; + bidder->options = NULL; + bidder->free = NULL; +#if HAVE_ZSTD_H && HAVE_LIBZSTD + return (ARCHIVE_OK); +#else + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external zstd program for zstd decompression"); + return (ARCHIVE_WARN); +#endif +} + +/* + * Test whether we can handle this data. + */ +static int +zstd_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + const unsigned char *buffer; + ssize_t avail; + unsigned prefix; + + /* Zstd frame magic values */ + const unsigned zstd_magic = 0xFD2FB528U; + + (void) self; /* UNUSED */ + + buffer = __archive_read_filter_ahead(filter, 4, &avail); + if (buffer == NULL) + return (0); + + prefix = archive_le32dec(buffer); + if (prefix == zstd_magic) + return (32); + + return (0); +} + +#if !(HAVE_ZSTD_H && HAVE_LIBZSTD) + +/* + * If we don't have the library on this system, we can't do the + * decompression directly. We can, however, try to run "zstd -d" + * in case that's available. + */ +static int +zstd_bidder_init(struct archive_read_filter *self) +{ + int r; + + r = __archive_read_program(self, "zstd -d -qq"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_FILTER_ZSTD; + self->name = "zstd"; + return (r); +} + +#else + +/* + * Initialize the filter object + */ +static int +zstd_bidder_init(struct archive_read_filter *self) +{ + struct private_data *state; + const size_t out_block_size = ZSTD_DStreamOutSize(); + void *out_block; + ZSTD_DStream *dstream; + + self->code = ARCHIVE_FILTER_ZSTD; + self->name = "zstd"; + + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = (unsigned char *)malloc(out_block_size); + dstream = ZSTD_createDStream(); + + if (state == NULL || out_block == NULL || dstream == NULL) { + free(out_block); + free(state); + ZSTD_freeDStream(dstream); /* supports free on NULL */ + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for zstd decompression"); + return (ARCHIVE_FATAL); + } + + self->data = state; + + state->out_block_size = out_block_size; + state->out_block = out_block; + state->dstream = dstream; + self->read = zstd_filter_read; + self->skip = NULL; /* not supported */ + self->close = zstd_filter_close; + + state->eof = 0; + state->in_frame = 0; + + return (ARCHIVE_OK); +} + +static ssize_t +zstd_filter_read(struct archive_read_filter *self, const void **p) +{ + struct private_data *state; + size_t decompressed; + ssize_t avail_in; + ZSTD_outBuffer out; + ZSTD_inBuffer in; + + state = (struct private_data *)self->data; + + out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 }; + + /* Try to fill the output buffer. */ + while (out.pos < out.size && !state->eof) { + if (!state->in_frame) { + const size_t ret = ZSTD_initDStream(state->dstream); + if (ZSTD_isError(ret)) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Error initializing zstd decompressor: %s", + ZSTD_getErrorName(ret)); + return (ARCHIVE_FATAL); + } + } + in.src = __archive_read_filter_ahead(self->upstream, 1, + &avail_in); + if (avail_in < 0) { + return avail_in; + } + if (in.src == NULL && avail_in == 0) { + if (!state->in_frame) { + /* end of stream */ + state->eof = 1; + break; + } else { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Truncated zstd input"); + return (ARCHIVE_FATAL); + } + } + in.size = avail_in; + in.pos = 0; + + { + const size_t ret = + ZSTD_decompressStream(state->dstream, &out, &in); + + if (ZSTD_isError(ret)) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "Zstd decompression failed: %s", + ZSTD_getErrorName(ret)); + return (ARCHIVE_FATAL); + } + + /* Decompressor made some progress */ + __archive_read_filter_consume(self->upstream, in.pos); + + /* ret guaranteed to be > 0 if frame isn't done yet */ + state->in_frame = (ret != 0); + } + } + + decompressed = out.pos; + state->total_out += decompressed; + if (decompressed == 0) + *p = NULL; + else + *p = state->out_block; + return (decompressed); +} + +/* + * Clean up the decompressor. + */ +static int +zstd_filter_close(struct archive_read_filter *self) +{ + struct private_data *state; + + state = (struct private_data *)self->data; + + ZSTD_freeDStream(state->dstream); + free(state->out_block); + free(state); + + return (ARCHIVE_OK); +} + +#endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */ diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c index 96774f4..a885a4c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c @@ -975,18 +975,6 @@ decode_codec_id(const unsigned char *codecId, size_t id_size) return (id); } -static void * -ppmd_alloc(void *p, size_t size) -{ - (void)p; - return malloc(size); -} -static void -ppmd_free(void *p, void *address) -{ - (void)p; - free(address); -} static Byte ppmd_read(void *p) { @@ -1006,8 +994,6 @@ ppmd_read(void *p) return (b); } -static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free }; - static int init_decompression(struct archive_read *a, struct _7zip *zip, const struct _7z_coder *coder1, const struct _7z_coder *coder2) @@ -1237,7 +1223,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( - &zip->ppmd7_context, &g_szalloc); + &zip->ppmd7_context); zip->ppmd7_valid = 0; } @@ -1256,7 +1242,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, } __archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context); r = __archive_ppmd7_functions.Ppmd7_Alloc( - &zip->ppmd7_context, msize, &g_szalloc); + &zip->ppmd7_context, msize); if (r == 0) { archive_set_error(&a->archive, ENOMEM, "Coludn't allocate memory for PPMd"); @@ -1636,7 +1622,7 @@ free_decompression(struct archive_read *a, struct _7zip *zip) #endif if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( - &zip->ppmd7_context, &g_szalloc); + &zip->ppmd7_context); zip->ppmd7_valid = 0; } return (r); @@ -2569,6 +2555,7 @@ read_Header(struct archive_read *a, struct _7z_header_info *h, case kDummy: if (ll == 0) break; + __LA_FALLTHROUGH; default: if (header_bytes(a, ll) == NULL) return (-1); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c index ad9f782..67d5b21 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c @@ -633,6 +633,13 @@ header_newc(struct archive_read *a, struct cpio *cpio, /* Pad name to 2 more than a multiple of 4. */ *name_pad = (2 - *namelength) & 3; + /* Make sure that the padded name length fits into size_t. */ + if (*name_pad > SIZE_MAX - *namelength) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "cpio archive has invalid namelength"); + return (ARCHIVE_FATAL); + } + /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c index 4b436d1..93649f8 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c @@ -409,7 +409,8 @@ static int next_entry_seek(struct archive_read *, struct iso9660 *, struct file_info **); static struct file_info * parse_file_info(struct archive_read *a, - struct file_info *parent, const unsigned char *isodirrec); + struct file_info *parent, const unsigned char *isodirrec, + size_t reclen); static int parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *start, const unsigned char *end); @@ -1022,7 +1023,7 @@ read_children(struct archive_read *a, struct file_info *parent) if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\001') continue; - child = parse_file_info(a, parent, p); + child = parse_file_info(a, parent, p, b - p); if (child == NULL) { __archive_read_consume(a, skip_size); return (ARCHIVE_FATAL); @@ -1112,7 +1113,7 @@ choose_volume(struct archive_read *a, struct iso9660 *iso9660) */ seenJoliet = iso9660->seenJoliet;/* Save flag. */ iso9660->seenJoliet = 0; - file = parse_file_info(a, NULL, block); + file = parse_file_info(a, NULL, block, vd->size); if (file == NULL) return (ARCHIVE_FATAL); iso9660->seenJoliet = seenJoliet; @@ -1144,7 +1145,7 @@ choose_volume(struct archive_read *a, struct iso9660 *iso9660) return (ARCHIVE_FATAL); } iso9660->seenJoliet = 0; - file = parse_file_info(a, NULL, block); + file = parse_file_info(a, NULL, block, vd->size); if (file == NULL) return (ARCHIVE_FATAL); iso9660->seenJoliet = seenJoliet; @@ -1749,7 +1750,7 @@ archive_read_format_iso9660_cleanup(struct archive_read *a) */ static struct file_info * parse_file_info(struct archive_read *a, struct file_info *parent, - const unsigned char *isodirrec) + const unsigned char *isodirrec, size_t reclen) { struct iso9660 *iso9660; struct file_info *file, *filep; @@ -1763,16 +1764,20 @@ parse_file_info(struct archive_read *a, struct file_info *parent, iso9660 = (struct iso9660 *)(a->format->data); - dr_len = (size_t)isodirrec[DR_length_offset]; - name_len = (size_t)isodirrec[DR_name_len_offset]; - location = archive_le32dec(isodirrec + DR_extent_offset); - fsize = toi(isodirrec + DR_size_offset, DR_size_size); - /* Sanity check that dr_len needs at least 34. */ - if (dr_len < 34) { + if (reclen != 0) + dr_len = (size_t)isodirrec[DR_length_offset]; + /* + * Sanity check that reclen is not zero and dr_len is greater than + * reclen but at least 34 + */ + if (reclen == 0 || reclen < dr_len || dr_len < 34) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid length of directory record"); + "Invalid length of directory record"); return (NULL); } + name_len = (size_t)isodirrec[DR_name_len_offset]; + location = archive_le32dec(isodirrec + DR_extent_offset); + fsize = toi(isodirrec + DR_size_offset, DR_size_size); /* Sanity check that name_len doesn't exceed dr_len. */ if (dr_len - 33 < name_len || name_len == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c index 1995e9c..98d02c6 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c @@ -701,6 +701,12 @@ archive_read_format_lha_read_header(struct archive_read *a, * Prepare variables used to read a file content. */ lha->entry_bytes_remaining = lha->compsize; + if (lha->entry_bytes_remaining < 0) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid LHa entry size"); + return (ARCHIVE_FATAL); + } lha->entry_offset = 0; lha->entry_crc_calculated = 0; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c index 44b6083..5b0eadc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #include "archive.h" #include "archive_entry.h" #include "archive_private.h" +#include "archive_rb.h" #include "archive_read_private.h" #include "archive_string.h" #include "archive_pack_dev.h" @@ -75,7 +76,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #define MTREE_HAS_OPTIONAL 0x0800 #define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ -#define MTREE_HASHTABLE_SIZE 1024 +#define MAX_LINE_LEN (1024 * 1024) struct mtree_option { struct mtree_option *next; @@ -83,13 +84,13 @@ struct mtree_option { }; struct mtree_entry { + struct archive_rb_node rbnode; + struct mtree_entry *next_dup; struct mtree_entry *next; struct mtree_option *options; char *name; char full; char used; - unsigned int name_hash; - struct mtree_entry *hashtable_next; }; struct mtree { @@ -102,11 +103,12 @@ struct mtree { const char *archive_format_name; struct mtree_entry *entries; struct mtree_entry *this_entry; - struct mtree_entry *entry_hashtable[MTREE_HASHTABLE_SIZE]; + struct archive_rb_tree entry_rbtree; struct archive_string current_dir; struct archive_string contents_name; struct archive_entry_linkresolver *resolver; + struct archive_rb_tree rbtree; int64_t cur_size; char checkfs; @@ -115,7 +117,6 @@ struct mtree { static int bid_keycmp(const char *, const char *, ssize_t); static int cleanup(struct archive_read *); static int detect_form(struct archive_read *, int *); -static unsigned int hash(const char *); static int mtree_bid(struct archive_read *, int); static int parse_file(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); @@ -217,9 +218,30 @@ free_options(struct mtree_option *head) } } +static int +mtree_cmp_node(const struct archive_rb_node *n1, + const struct archive_rb_node *n2) +{ + const struct mtree_entry *e1 = (const struct mtree_entry *)n1; + const struct mtree_entry *e2 = (const struct mtree_entry *)n2; + + return (strcmp(e1->name, e2->name)); +} + +static int +mtree_cmp_key(const struct archive_rb_node *n, const void *key) +{ + const struct mtree_entry *e = (const struct mtree_entry *)n; + + return (strcmp(e->name, key)); +} + int archive_read_support_format_mtree(struct archive *_a) { + static const struct archive_rb_tree_ops rb_ops = { + mtree_cmp_node, mtree_cmp_key, + }; struct archive_read *a = (struct archive_read *)_a; struct mtree *mtree; int r; @@ -235,6 +257,8 @@ archive_read_support_format_mtree(struct archive *_a) } mtree->fd = -1; + __archive_rb_tree_init(&mtree->rbtree, &rb_ops); + r = __archive_read_register_format(a, mtree, "mtree", mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL); @@ -334,6 +358,14 @@ next_line(struct archive_read *a, size_t nbytes_req = (*ravail+1023) & ~1023U; ssize_t tested; + /* + * Place an arbitrary limit on the line length. + * mtree is almost free-form input and without line length limits, + * it can consume a lot of memory. + */ + if (len >= MAX_LINE_LEN) + return (-1); + /* Increase reading bytes if it is not enough to at least * new two lines. */ if (nbytes_req < (size_t)*ravail + 160) @@ -865,12 +897,11 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_option **global, const char *line, ssize_t line_len, struct mtree_entry **last_entry, int is_form_d) { - struct mtree_entry *entry, *ht_iter; + struct mtree_entry *entry; struct mtree_option *iter; const char *next, *eq, *name, *end; size_t name_len, len; int r, i; - unsigned int ht_idx; if ((entry = malloc(sizeof(*entry))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); @@ -881,8 +912,6 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, entry->name = NULL; entry->used = 0; entry->full = 0; - entry->name_hash = 0; - entry->hashtable_next = NULL; /* Add this entry to list. */ if (*last_entry == NULL) @@ -935,15 +964,17 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, memcpy(entry->name, name, name_len); entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); - entry->name_hash = hash(entry->name); - ht_idx = entry->name_hash % MTREE_HASHTABLE_SIZE; - if ((ht_iter = mtree->entry_hashtable[ht_idx]) != NULL) { - while (ht_iter->hashtable_next) - ht_iter = ht_iter->hashtable_next; - ht_iter->hashtable_next = entry; - } else { - mtree->entry_hashtable[ht_idx] = entry; + entry->next_dup = NULL; + if (entry->full) { + if (!__archive_rb_tree_insert_node(&mtree->rbtree, &entry->rbnode)) { + struct mtree_entry *alt; + alt = (struct mtree_entry *)__archive_rb_tree_find_node( + &mtree->rbtree, entry->name); + while (alt->next_dup) + alt = alt->next_dup; + alt->next_dup = entry; + } } for (iter = *global; iter != NULL; iter = iter->next) { @@ -1138,14 +1169,13 @@ parse_file(struct archive_read *a, struct archive_entry *entry, * with pathname canonicalization, which is a very * tricky subject.) */ - for (mp = mentry->hashtable_next; mp != NULL; mp = mp->hashtable_next) { - if (mp->full && !mp->used - && mentry->name_hash == mp->name_hash - && strcmp(mentry->name, mp->name) == 0) { + mp = (struct mtree_entry *)__archive_rb_tree_find_node( + &mtree->rbtree, mentry->name); + for (; mp; mp = mp->next_dup) { + if (mp->full && !mp->used) { /* Later lines override earlier ones. */ mp->used = 1; - r1 = parse_line(a, entry, mtree, mp, - &parsed_kws); + r1 = parse_line(a, entry, mtree, mp, &parsed_kws); if (r1 < r) r = r1; } @@ -1489,6 +1519,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, } if (strcmp(key, "cksum") == 0) break; + __LA_FALLTHROUGH; case 'd': if (strcmp(key, "device") == 0) { /* stat(2) st_rdev field, e.g. the major/minor IDs @@ -1502,12 +1533,14 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_entry_set_rdev(entry, dev); return r; } + __LA_FALLTHROUGH; case 'f': if (strcmp(key, "flags") == 0) { *parsed_kws |= MTREE_HAS_FFLAGS; archive_entry_copy_fflags_text(entry, val); break; } + __LA_FALLTHROUGH; case 'g': if (strcmp(key, "gid") == 0) { *parsed_kws |= MTREE_HAS_GID; @@ -1519,16 +1552,19 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_entry_copy_gname(entry, val); break; } + __LA_FALLTHROUGH; case 'i': if (strcmp(key, "inode") == 0) { archive_entry_set_ino(entry, mtree_atol(&val, 10)); break; } + __LA_FALLTHROUGH; case 'l': if (strcmp(key, "link") == 0) { archive_entry_copy_symlink(entry, val); break; } + __LA_FALLTHROUGH; case 'm': if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) break; @@ -1545,6 +1581,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, } break; } + __LA_FALLTHROUGH; case 'n': if (strcmp(key, "nlink") == 0) { *parsed_kws |= MTREE_HAS_NLINK; @@ -1552,6 +1589,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, (unsigned int)mtree_atol(&val, 10)); break; } + __LA_FALLTHROUGH; case 'r': if (strcmp(key, "resdevice") == 0) { /* stat(2) st_dev field, e.g. the device ID where the @@ -1567,6 +1605,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, if (strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) break; + __LA_FALLTHROUGH; case 's': if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) break; @@ -1583,6 +1622,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_entry_set_size(entry, mtree_atol(&val, 10)); break; } + __LA_FALLTHROUGH; case 't': if (strcmp(key, "tags") == 0) { /* @@ -1625,18 +1665,21 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_entry_set_filetype(entry, AE_IFBLK); break; } + __LA_FALLTHROUGH; case 'c': if (strcmp(val, "char") == 0) { archive_entry_set_filetype(entry, AE_IFCHR); break; } + __LA_FALLTHROUGH; case 'd': if (strcmp(val, "dir") == 0) { archive_entry_set_filetype(entry, AE_IFDIR); break; } + __LA_FALLTHROUGH; case 'f': if (strcmp(val, "fifo") == 0) { archive_entry_set_filetype(entry, @@ -1648,12 +1691,14 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, AE_IFREG); break; } + __LA_FALLTHROUGH; case 'l': if (strcmp(val, "link") == 0) { archive_entry_set_filetype(entry, AE_IFLNK); break; } + __LA_FALLTHROUGH; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -1665,6 +1710,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, *parsed_kws |= MTREE_HAS_TYPE; break; } + __LA_FALLTHROUGH; case 'u': if (strcmp(key, "uid") == 0) { *parsed_kws |= MTREE_HAS_UID; @@ -1676,6 +1722,7 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, archive_entry_copy_uname(entry, val); break; } + __LA_FALLTHROUGH; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized key %s=%s", key, val); @@ -1962,19 +2009,3 @@ readline(struct archive_read *a, struct mtree *mtree, char **start, find_off = u - mtree->line.s; } } - -static unsigned int -hash(const char *p) -{ - /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, - as used by ELF for hashing function names. */ - unsigned g, h = 0; - while (*p != '\0') { - h = (h << 4) + *p++; - if ((g = h & 0xF0000000) != 0) { - h ^= g >> 24; - h &= 0x0FFFFFFF; - } - } - return h; -} diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c index a0e641e..c68d77b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c @@ -604,20 +604,6 @@ lzss_emit_match(struct rar *rar, int offset, int length) rar->lzss.position += length; } -static void * -ppmd_alloc(void *p, size_t size) -{ - (void)p; - return malloc(size); -} -static void -ppmd_free(void *p, void *address) -{ - (void)p; - free(address); -} -static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free }; - static Byte ppmd_read(void *p) { @@ -1038,7 +1024,7 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, case COMPRESS_METHOD_BEST: ret = read_data_compressed(a, buff, size, offset); if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) - __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc); + __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); break; default: @@ -1253,7 +1239,7 @@ archive_read_format_rar_cleanup(struct archive_read *a) free(rar->dbo); free(rar->unp_buffer); free(rar->lzss.window); - __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc); + __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); free(rar); (a->format->data) = NULL; return (ARCHIVE_OK); @@ -1496,7 +1482,11 @@ read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } filename[filename_size++] = '\0'; - filename[filename_size++] = '\0'; + /* + * Do not increment filename_size here as the computations below + * add the space for the terminating NUL explicitly. + */ + filename[filename_size] = '\0'; /* Decoded unicode form is UTF-16BE, so we have to update a string * conversion object for it. */ @@ -1654,7 +1644,7 @@ read_header(struct archive_read *a, struct archive_entry *entry, rar->unp_offset = 0; rar->unp_buffer_size = UNP_BUFFER_SIZE; memset(rar->lengthtable, 0, sizeof(rar->lengthtable)); - __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc); + __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->ppmd_valid = rar->ppmd_eod = 0; /* Don't set any archive entries for non-file header types */ @@ -2118,7 +2108,7 @@ parse_codes(struct archive_read *a) /* Make sure ppmd7_contest is freed before Ppmd7_Construct * because reading a broken file cause this abnormal sequence. */ - __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc); + __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context); rar->bytein.a = a; rar->bytein.Read = &ppmd_read; @@ -2133,7 +2123,7 @@ parse_codes(struct archive_read *a) } if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context, - rar->dictionary_size, &g_szalloc)) + rar->dictionary_size)) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c index 30d5bc8..60800bb 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c @@ -251,15 +251,15 @@ archive_read_support_format_tar(struct archive *_a) ARCHIVE_STATE_NEW, "archive_read_support_format_tar"); tar = (struct tar *)calloc(1, sizeof(*tar)); -#ifdef HAVE_COPYFILE_H - /* Set this by default on Mac OS. */ - tar->process_mac_extensions = 1; -#endif if (tar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } +#ifdef HAVE_COPYFILE_H + /* Set this by default on Mac OS. */ + tar->process_mac_extensions = 1; +#endif r = __archive_read_register_format(a, tar, "tar", archive_read_format_tar_bid, @@ -2241,7 +2241,7 @@ gnu_add_sparse_entry(struct archive_read *a, struct tar *tar, else tar->sparse_list = p; tar->sparse_last = p; - if (remaining < 0 || offset < 0) { + if (remaining < 0 || offset < 0 || offset > INT64_MAX - remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data"); return (ARCHIVE_FATAL); } diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c index eeac1c8..9292ed7 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c @@ -1040,6 +1040,9 @@ atol10(const char *p, size_t char_cnt) uint64_t l; int digit; + if (char_cnt == 0) + return (0); + l = 0; digit = *p - '0'; while (digit >= 0 && digit < 10 && char_cnt-- > 0) { @@ -1054,7 +1057,10 @@ atol8(const char *p, size_t char_cnt) { int64_t l; int digit; - + + if (char_cnt == 0) + return (0); + l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') @@ -2623,6 +2629,14 @@ strappend_base64(struct xar *xar, archive_strncat(as, (const char *)buff, len); } +static int +is_string(const char *known, const char *data, size_t len) +{ + if (strlen(known) != len) + return -1; + return memcmp(data, known, len); +} + static void xml_data(void *userData, const char *s, int len) { @@ -2674,26 +2688,26 @@ xml_data(void *userData, const char *s, int len) archive_strncpy(&(xar->file->symlink), s, len); break; case FILE_TYPE: - if (strncmp("file", s, len) == 0 || - strncmp("hardlink", s, len) == 0) + if (is_string("file", s, len) == 0 || + is_string("hardlink", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFREG; - if (strncmp("directory", s, len) == 0) + if (is_string("directory", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFDIR; - if (strncmp("symlink", s, len) == 0) + if (is_string("symlink", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFLNK; - if (strncmp("character special", s, len) == 0) + if (is_string("character special", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFCHR; - if (strncmp("block special", s, len) == 0) + if (is_string("block special", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFBLK; - if (strncmp("socket", s, len) == 0) + if (is_string("socket", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFSOCK; - if (strncmp("fifo", s, len) == 0) + if (is_string("fifo", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFIFO; xar->file->has |= HAS_TYPE; diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c index ddd4458..7e99b12 100644 --- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c @@ -511,7 +511,13 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct case 0x5455: { /* Extended time field "UT". */ - int flags = p[offset]; + int flags; + if (datasize == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Incomplete extended time field"); + return ARCHIVE_FAILED; + } + flags = p[offset]; offset++; datasize--; /* Flag bits indicate which dates are present. */ @@ -723,6 +729,11 @@ process_extra(struct archive_read *a, const char *p, size_t extra_length, struct } case 0x9901: /* WinZip AES extra data field. */ + if (datasize < 6) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Incomplete AES field"); + return ARCHIVE_FAILED; + } if (p[offset + 2] == 'A' && p[offset + 3] == 'E') { /* Vendor version. */ zip_entry->aes_extra.vendor = @@ -881,6 +892,24 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip_entry->mode |= 0664; } + /* Windows archivers sometimes use backslash as the directory separator. + Normalize to slash. */ + if (zip_entry->system == 0 && + (wp = archive_entry_pathname_w(entry)) != NULL) { + if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) { + size_t i; + struct archive_wstring s; + archive_string_init(&s); + archive_wstrcpy(&s, wp); + for (i = 0; i < archive_strlen(&s); i++) { + if (s.s[i] == '\\') + s.s[i] = '/'; + } + archive_entry_copy_pathname_w(entry, s.s); + archive_wstring_free(&s); + } + } + /* Make sure that entries with a trailing '/' are marked as directories * even if the External File Attributes contains bogus values. If this * is not a directory and there is no type, assume regularfile. */ @@ -1056,6 +1085,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip->end_of_entry = 1; /* Set up a more descriptive format name. */ + archive_string_empty(&zip->format_name); archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)", version / 10, version % 10, compression_name(zip->entry->compression)); diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c index 5ae09b6..554533e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_string.c +++ b/Utilities/cmlibarchive/libarchive/archive_string.c @@ -214,7 +214,8 @@ archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s) { if (archive_wstring_ensure(as, as->length + s + 1) == NULL) return (NULL); - wmemmove(as->s + as->length, p, s); + if (s) + wmemmove(as->s + as->length, p, s); as->length += s; as->s[as->length] = 0; return (as); diff --git a/Utilities/cmlibarchive/libarchive/archive_util.c b/Utilities/cmlibarchive/libarchive/archive_util.c index 1e36ad7..e5c6e3b 100644 --- a/Utilities/cmlibarchive/libarchive/archive_util.c +++ b/Utilities/cmlibarchive/libarchive/archive_util.c @@ -140,7 +140,7 @@ archive_compression_name(struct archive *a) /* * Return a count of the number of compressed bytes processed. */ -int64_t +la_int64_t archive_position_compressed(struct archive *a) { return archive_filter_bytes(a, -1); @@ -149,7 +149,7 @@ archive_position_compressed(struct archive *a) /* * Return a count of the number of uncompressed bytes processed. */ -int64_t +la_int64_t archive_position_uncompressed(struct archive *a) { return archive_filter_bytes(a, 0); diff --git a/Utilities/cmlibarchive/libarchive/archive_version_details.c b/Utilities/cmlibarchive/libarchive/archive_version_details.c index 9289bf1..e773e5e 100644 --- a/Utilities/cmlibarchive/libarchive/archive_version_details.c +++ b/Utilities/cmlibarchive/libarchive/archive_version_details.c @@ -45,6 +45,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #ifdef HAVE_LZ4_H #include <lz4.h> #endif +#ifdef HAVE_ZSTD_H +#include <zstd.h> +#endif #include "archive.h" #include "archive_private.h" @@ -59,6 +62,7 @@ archive_version_details(void) const char *liblzma = archive_liblzma_version(); const char *bzlib = archive_bzlib_version(); const char *liblz4 = archive_liblz4_version(); + const char *libzstd = archive_libzstd_version(); if (!init) { archive_string_init(&str); @@ -84,6 +88,10 @@ archive_version_details(void) archive_strcat(&str, " liblz4/"); archive_strcat(&str, liblz4); } + if (libzstd) { + archive_strcat(&str, " libzstd/"); + archive_strcat(&str, libzstd); + } } return str.s; } @@ -131,3 +139,13 @@ archive_liblz4_version(void) return NULL; #endif } + +const char * +archive_libzstd_version(void) +{ +#if HAVE_ZSTD_H && HAVE_LIBZSTD + return ZSTD_VERSION_STRING; +#else + return NULL; +#endif +} diff --git a/Utilities/cmlibarchive/libarchive/archive_virtual.c b/Utilities/cmlibarchive/libarchive/archive_virtual.c index de2595a..f509ee5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_virtual.c +++ b/Utilities/cmlibarchive/libarchive/archive_virtual.c @@ -48,7 +48,7 @@ archive_filter_name(struct archive *a, int n) return ((a->vtable->archive_filter_name)(a, n)); } -int64_t +la_int64_t archive_filter_bytes(struct archive *a, int n) { return ((a->vtable->archive_filter_bytes)(a, n)); @@ -124,14 +124,15 @@ archive_write_finish_entry(struct archive *a) return ((a->vtable->archive_write_finish_entry)(a)); } -ssize_t +la_ssize_t archive_write_data(struct archive *a, const void *buff, size_t s) { return ((a->vtable->archive_write_data)(a, buff, s)); } -ssize_t -archive_write_data_block(struct archive *a, const void *buff, size_t s, int64_t o) +la_ssize_t +archive_write_data_block(struct archive *a, const void *buff, size_t s, + la_int64_t o) { if (a->vtable->archive_write_data_block == NULL) { archive_set_error(a, ARCHIVE_ERRNO_MISC, @@ -156,7 +157,7 @@ archive_read_next_header2(struct archive *a, struct archive_entry *entry) int archive_read_data_block(struct archive *a, - const void **buff, size_t *s, int64_t *o) + const void **buff, size_t *s, la_int64_t *o) { return ((a->vtable->archive_read_data_block)(a, buff, s, o)); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write.3 b/Utilities/cmlibarchive/libarchive/archive_write.3 index 376d71d..c1164f5 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write.3 @@ -71,7 +71,7 @@ support. .\" .Ss Set options See -.Xr archive_read_set_options 3 . +.Xr archive_write_set_options 3 . .\" .Ss Open archive See diff --git a/Utilities/cmlibarchive/libarchive/archive_write.c b/Utilities/cmlibarchive/libarchive/archive_write.c index 0634a22..e8daf53 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write.c +++ b/Utilities/cmlibarchive/libarchive/archive_write.c @@ -190,7 +190,7 @@ archive_write_get_bytes_in_last_block(struct archive *_a) * an archive to itself recursively. */ int -archive_write_set_skip_file(struct archive *_a, int64_t d, int64_t i) +archive_write_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i) { struct archive_write *a = (struct archive_write *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c index 08f518a..203f414 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter.c @@ -53,6 +53,7 @@ struct { int code; int (*setter)(struct archive *); } codes[] = { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip }, { ARCHIVE_FILTER_UU, archive_write_add_filter_uuencode }, { ARCHIVE_FILTER_XZ, archive_write_add_filter_xz }, + { ARCHIVE_FILTER_ZSTD, archive_write_add_filter_zstd }, { -1, NULL } }; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c index 85a8d47..ffa633c 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_by_name.c @@ -57,6 +57,7 @@ struct { const char *name; int (*setter)(struct archive *); } names[] = { "lzop", archive_write_add_filter_lzop }, { "uuencode", archive_write_add_filter_uuencode }, { "xz", archive_write_add_filter_xz }, + { "zstd", archive_write_add_filter_zstd }, { NULL, NULL } }; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c index bbcc178..986123a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_gzip.c @@ -226,7 +226,12 @@ archive_compressor_gzip_open(struct archive_write_filter *f) data->compressed[7] = (uint8_t)(t>>24)&0xff; } else memset(&data->compressed[4], 0, 4); - data->compressed[8] = 0; /* No deflate options */ + if (data->compression_level == 9) + data->compressed[8] = 2; + else if(data->compression_level == 1) + data->compressed[8] = 4; + else + data->compressed[8] = 0; data->compressed[9] = 3; /* OS=Unix */ data->stream.next_out += 10; data->stream.avail_out -= 10; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c new file mode 100644 index 0000000..671fc6a --- /dev/null +++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c @@ -0,0 +1,335 @@ +/*- + * Copyright (c) 2017 Sean Purcell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" + +__FBSDID("$FreeBSD$"); + + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_ZSTD_H +#include <zstd.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_string.h" +#include "archive_write_private.h" + +/* Don't compile this if we don't have zstd.h */ + +struct private_data { + int compression_level; +#if HAVE_ZSTD_H && HAVE_LIBZSTD + ZSTD_CStream *cstream; + int64_t total_in; + ZSTD_outBuffer out; +#else + struct archive_write_program_data *pdata; +#endif +}; + +static int archive_compressor_zstd_options(struct archive_write_filter *, + const char *, const char *); +static int archive_compressor_zstd_open(struct archive_write_filter *); +static int archive_compressor_zstd_write(struct archive_write_filter *, + const void *, size_t); +static int archive_compressor_zstd_close(struct archive_write_filter *); +static int archive_compressor_zstd_free(struct archive_write_filter *); +#if HAVE_ZSTD_H && HAVE_LIBZSTD +static int drive_compressor(struct archive_write_filter *, + struct private_data *, int, const void *, size_t); +#endif + + +/* + * Add a zstd compression filter to this write handle. + */ +int +archive_write_add_filter_zstd(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_data *data; + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd"); + + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + f->data = data; + f->open = &archive_compressor_zstd_open; + f->options = &archive_compressor_zstd_options; + f->close = &archive_compressor_zstd_close; + f->free = &archive_compressor_zstd_free; + f->code = ARCHIVE_FILTER_ZSTD; + f->name = "zstd"; + data->compression_level = 3; /* Default level used by the zstd CLI */ +#if HAVE_ZSTD_H && HAVE_LIBZSTD + data->cstream = ZSTD_createCStream(); + if (data->cstream == NULL) { + free(data); + archive_set_error(&a->archive, ENOMEM, + "Failed to allocate zstd compressor object"); + return (ARCHIVE_FATAL); + } + + return (ARCHIVE_OK); +#else + data->pdata = __archive_write_program_allocate("zstd"); + if (data->pdata == NULL) { + free(data); + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Using external zstd program"); + return (ARCHIVE_WARN); +#endif +} + +static int +archive_compressor_zstd_free(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; +#if HAVE_ZSTD_H && HAVE_LIBZSTD + ZSTD_freeCStream(data->cstream); + free(data->out.dst); +#else + __archive_write_program_free(data->pdata); +#endif + free(data); + f->data = NULL; + return (ARCHIVE_OK); +} + +/* + * Set write options. + */ +static int +archive_compressor_zstd_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + struct private_data *data = (struct private_data *)f->data; + + if (strcmp(key, "compression-level") == 0) { + int level = atoi(value); +#if HAVE_ZSTD_H && HAVE_LIBZSTD + if (level < 1 || level > ZSTD_maxCLevel()) { +#else + /* If we don't have the library, hard-code the max level */ + if (level < 1 || level > 22) { +#endif + return (ARCHIVE_WARN); + } + data->compression_level = level; + return (ARCHIVE_OK); + } + + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if no one used this option. */ + return (ARCHIVE_WARN); +} + +#if HAVE_ZSTD_H && HAVE_LIBZSTD +/* + * Setup callback. + */ +static int +archive_compressor_zstd_open(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + + if (data->out.dst == NULL) { + size_t bs = ZSTD_CStreamOutSize(), bpb; + if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { + /* Buffer size should be a multiple number of + * the of bytes per block for performance. */ + bpb = archive_write_get_bytes_per_block(f->archive); + if (bpb > bs) + bs = bpb; + else if (bpb != 0) + bs -= bs % bpb; + } + data->out.size = bs; + data->out.pos = 0; + data->out.dst + = (unsigned char *)malloc(data->out.size); + if (data->out.dst == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for compression buffer"); + return (ARCHIVE_FATAL); + } + } + + f->write = archive_compressor_zstd_write; + + if (ZSTD_isError(ZSTD_initCStream(data->cstream, + data->compression_level))) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing zstd compressor object"); + return (ARCHIVE_FATAL); + } + + return (ARCHIVE_OK); +} + +/* + * Write data to the compressed stream. + */ +static int +archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, + size_t length) +{ + struct private_data *data = (struct private_data *)f->data; + int ret; + + /* Update statistics */ + data->total_in += length; + + if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK) + return (ret); + + return (ARCHIVE_OK); +} + +/* + * Finish the compression... + */ +static int +archive_compressor_zstd_close(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + int r1, r2; + + /* Finish zstd frame */ + r1 = drive_compressor(f, data, 1, NULL, 0); + + r2 = __archive_write_close_filter(f->next_filter); + + return r1 < r2 ? r1 : r2; +} + +/* + * Utility function to push input data through compressor, + * writing full output blocks as necessary. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write_filter *f, + struct private_data *data, int finishing, const void *src, size_t length) +{ + ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 }; + + for (;;) { + if (data->out.pos == data->out.size) { + const int ret = __archive_write_filter(f->next_filter, + data->out.dst, data->out.size); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + data->out.pos = 0; + } + + /* If there's nothing to do, we're done. */ + if (!finishing && in.pos == in.size) + return (ARCHIVE_OK); + + { + const size_t zstdret = !finishing ? + ZSTD_compressStream(data->cstream, &data->out, &in) + : ZSTD_endStream(data->cstream, &data->out); + + if (ZSTD_isError(zstdret)) { + archive_set_error(f->archive, + ARCHIVE_ERRNO_MISC, + "Zstd compression failed: %s", + ZSTD_getErrorName(zstdret)); + return (ARCHIVE_FATAL); + } + + /* If we're finishing, 0 means nothing left to flush */ + if (finishing && zstdret == 0) { + const int ret = __archive_write_filter(f->next_filter, + data->out.dst, data->out.pos); + return (ret); + } + } + } +} + +#else /* HAVE_ZSTD_H && HAVE_LIBZSTD */ + +static int +archive_compressor_zstd_open(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + struct archive_string as; + int r; + + archive_string_init(&as); + archive_string_sprintf(&as, "zstd -%d", data->compression_level); + + f->write = archive_compressor_zstd_write; + r = __archive_write_program_open(f, data->pdata, as.s); + archive_string_free(&as); + return (r); +} + +static int +archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff, + size_t length) +{ + struct private_data *data = (struct private_data *)f->data; + + return __archive_write_program_write(f, data->pdata, buff, length); +} + +static int +archive_compressor_zstd_close(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + + return __archive_write_program_close(f, data->pdata); +} + +#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD */ diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c index 4a42a3b..affa503 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c @@ -835,7 +835,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) } int -archive_write_disk_set_skip_file(struct archive *_a, int64_t d, int64_t i) +archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -1786,7 +1786,7 @@ finish_metadata: int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, - int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid), + la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid), void (*cleanup_gid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; @@ -1822,7 +1822,7 @@ archive_write_disk_set_user_lookup(struct archive *_a, } int64_t -archive_write_disk_gid(struct archive *_a, const char *name, int64_t id) +archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -1833,7 +1833,7 @@ archive_write_disk_gid(struct archive *_a, const char *name, int64_t id) } int64_t -archive_write_disk_uid(struct archive *_a, const char *name, int64_t id) +archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -1981,6 +1981,10 @@ restore_entry(struct archive_write_disk *a) if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ + if (S_ISDIR(a->mode)) { + /* Don't overwrite any settings on existing directories. */ + a->todo = 0; + } archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c index 94b016e..78eda4a 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c @@ -906,7 +906,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) } int -archive_write_disk_set_skip_file(struct archive *_a, int64_t d, int64_t i) +archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -1156,7 +1156,7 @@ _archive_write_disk_finish_entry(struct archive *_a) int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, - int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid), + la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid), void (*cleanup_gid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; @@ -1192,7 +1192,7 @@ archive_write_disk_set_user_lookup(struct archive *_a, } int64_t -archive_write_disk_gid(struct archive *_a, const char *name, int64_t id) +archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -1203,7 +1203,7 @@ archive_write_disk_gid(struct archive *_a, const char *name, int64_t id) } int64_t -archive_write_disk_uid(struct archive *_a, const char *name, int64_t id) +archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -1322,9 +1322,20 @@ restore_entry(struct archive_write_disk *a) } } + if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { + archive_set_error(&a->archive, en, + "Hard-link target '%s' does not exist.", + archive_entry_hardlink(a->entry)); + return (ARCHIVE_FAILED); + } + if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ + if (S_ISDIR(a->mode)) { + /* Don't overwrite any settings on existing directories. */ + a->todo = 0; + } archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_filter.3 b/Utilities/cmlibarchive/libarchive/archive_write_filter.3 index e1d1891..d6fa071 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_filter.3 +++ b/Utilities/cmlibarchive/libarchive/archive_write_filter.3 @@ -42,7 +42,8 @@ .Nm archive_write_add_filter_none , .Nm archive_write_add_filter_program , .Nm archive_write_add_filter_uuencode , -.Nm archive_write_add_filter_xz +.Nm archive_write_add_filter_xz , +.Nm archive_write_add_filter_zstd , .Nd functions enabling output filters .Sh LIBRARY Streaming Archive Library (libarchive, -larchive) @@ -76,6 +77,8 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_write_add_filter_uuencode "struct archive *" .Ft int .Fn archive_write_add_filter_xz "struct archive *" +.Ft int +.Fn archive_write_add_filter_zstd "struct archive *" .Sh DESCRIPTION .Bl -tag -width indent .It Xo @@ -89,6 +92,7 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_write_add_filter_lzma , .Fn archive_write_add_filter_lzop , .Fn archive_write_add_filter_xz , +.Fn archive_write_add_filter_zstd , .Xc The resulting archive will be compressed as specified. Note that the compressed output is always properly blocked. diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c index 3fc5a07..2bd4ec4 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c @@ -2095,19 +2095,6 @@ compression_init_encoder_lzma2(struct archive *a, /* * _7_PPMD compressor. */ -static void * -ppmd_alloc(void *p, size_t size) -{ - (void)p; - return malloc(size); -} -static void -ppmd_free(void *p, void *address) -{ - (void)p; - free(address); -} -static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free }; static void ppmd_write(void *p, Byte b) { @@ -2167,7 +2154,7 @@ compression_init_encoder_ppmd(struct archive *a, archive_le32enc(props+1, msize); __archive_ppmd7_functions.Ppmd7_Construct(&strm->ppmd7_context); r = __archive_ppmd7_functions.Ppmd7_Alloc( - &strm->ppmd7_context, msize, &g_szalloc); + &strm->ppmd7_context, msize); if (r == 0) { free(strm->buff); free(strm); @@ -2243,7 +2230,7 @@ compression_end_ppmd(struct archive *a, struct la_zstream *lastrm) (void)a; /* UNUSED */ strm = (struct ppmd_stream *)lastrm->real_stream; - __archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context, &g_szalloc); + __archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context); free(strm->buff); free(strm); lastrm->real_stream = NULL; diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c index c9771d8..50305cc 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_ar.c @@ -180,7 +180,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) } memset(buff, ' ', 60); - strncpy(&buff[AR_fmag_offset], "`\n", 2); + memcpy(&buff[AR_fmag_offset], "`\n", 2); if (strcmp(pathname, "/") == 0 ) { /* Entry is archive symbol table in GNU format */ @@ -189,7 +189,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) } if (strcmp(pathname, "__.SYMDEF") == 0) { /* Entry is archive symbol table in BSD format */ - strncpy(buff + AR_name_offset, "__.SYMDEF", 9); + memcpy(buff + AR_name_offset, "__.SYMDEF", 9); goto stat; } if (strcmp(pathname, "//") == 0) { @@ -225,7 +225,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) * actually 15 bytes. */ if (strlen(filename) <= 15) { - strncpy(&buff[AR_name_offset], + memcpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = '/'; } else { @@ -248,7 +248,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_FATAL); } - strncpy(se, filename, strlen(filename)); + memcpy(se, filename, strlen(filename)); strcpy(se + strlen(filename), "/\n"); ss = strstr(ar->strtab, se); @@ -285,11 +285,11 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) * archive header. */ if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { - strncpy(&buff[AR_name_offset], filename, strlen(filename)); + memcpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = ' '; } else { - strncpy(buff + AR_name_offset, "#1/", 3); + memcpy(buff + AR_name_offset, "#1/", 3); if (format_decimal(strlen(filename), buff + AR_name_offset + 3, AR_name_size - 3)) { @@ -374,13 +374,14 @@ archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) return (ARCHIVE_WARN); } - ar->strtab = (char *)malloc(s); + ar->strtab = (char *)malloc(s + 1); if (ar->strtab == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate strtab buffer"); return (ARCHIVE_FATAL); } - strncpy(ar->strtab, buff, s); + memcpy(ar->strtab, buff, s); + ar->strtab[s] = '\0'; ar->has_strtab = 1; } diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c index 0eaf733..3cebeae 100644 --- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c +++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c @@ -1654,7 +1654,7 @@ build_pax_attribute_name(char *dest, const char *src) * GNU PAX Format 1.0 requires the special name, which pattern is: * <dir>/GNUSparseFile.<pid>/<original file name> * - * Since reproducable archives are more important, use 0 as pid. + * Since reproducible archives are more important, use 0 as pid. * * This function is used for only Sparse file, a file type of which * is regular file. |