Merge topic 'instrumentation-interrupt'

b5c03bdef4 Source: Restore compilation on FreeBSD

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !12196
diff --git a/.pvsconfig b/.pvsconfig
index 4d7413f..2cb7888 100644
--- a/.pvsconfig
+++ b/.pvsconfig
@@ -6,17 +6,13 @@
 //-V::508
 # The 'x' variable is assigned values twice successively. Perhaps this is a mistake.
 //-V::519
-# Possible null pointer dereference.
-//-V::522
 # Constant value is represented by an octal form.
 //-V::536
 # Iterators are passed as arguments to 'Foo' function. Consider inspecting the expression.
 //-V::539
 # Expression is always true/false.
 //-V::547
-# Expression of the 'A - B > 0' kind will work as 'A != B'.
-//-V::555
-# Possible array overrun.
+# Array underrun/overrun is possible.
 //-V::557
 # Part of conditional expression is always true/false.
 //-V::560
@@ -90,8 +86,6 @@
 //-V::1071
 # Conditional initialization inside the constructor may leave some members uninitialized.
 //-V::1077
-# Call of the 'Foo' function will lead to buffer underflow.
-//-V::1086
 # Waiting on condition variable without predicate. A thread can wait indefinitely or experience a spurious wake-up.
 //-V::1089
 # The 'emplace' / 'insert' function call contains potentially dangerous move operation. Moved object can be destroyed even if there is no insertion.
diff --git a/Help/command/export.rst b/Help/command/export.rst
index 775282f..5b98ecf 100644
--- a/Help/command/export.rst
+++ b/Help/command/export.rst
@@ -178,7 +178,8 @@
          [FORMAT <string>]
          [PROJECT <project-name>|NO_PROJECT_METADATA]
          [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
-         [LICENSE <license-string>]
+         [DATA_LICENSE <license-string>]
+         [DEFAULT_LICENSE <license-string>]
          [DESCRIPTION <description-string>]
          [HOMEPAGE_URL <url-string>]
          [PACKAGE_URL <url-string>])
diff --git a/Help/command/file.rst b/Help/command/file.rst
index 094262e..0b9fe56 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -937,6 +937,7 @@
     [MTIME <mtime>]
     [THREADS <number>]
     [WORKING_DIRECTORY <dir>]
+    [PATTERNS_EXCLUDE <pattern>...]
     [VERBOSE])
   :target: ARCHIVE_CREATE
   :break: verbatim
@@ -1077,6 +1078,17 @@
     this directory.  If this option is not provided, the current working
     directory will be used by default.
 
+  ``PATTERNS_EXCLUDE <pattern>...``
+    .. versionadded:: 4.5
+
+    Do not add files or directories that match one of the given patterns.
+    Wildcards are supported.  When a directory matches a pattern, it is
+    excluded together with everything beneath it.
+
+    Exclusion patterns are not anchored to the start of an entry's path: a
+    pattern matches if it matches any portion of the path.  This is consistent
+    with the ``--exclude`` option of command-line ``tar``.
+
   ``VERBOSE``
     Enable verbose output from the archive operation.
 
diff --git a/Help/command/function.rst b/Help/command/function.rst
index 069f9fa..6722ca4 100644
--- a/Help/command/function.rst
+++ b/Help/command/function.rst
@@ -59,20 +59,29 @@
 
 When the function is invoked, the recorded ``<commands>`` are first
 modified by replacing formal parameters (``${arg1}``, ...) with the
-arguments passed, and then invoked as normal commands.
+arguments passed, and then invoked as normal commands. The following
+variables are set in the scope of the function:
 
-In addition to referencing the formal parameters you can reference the
-``ARGC`` variable which will be set to the number of arguments passed
-into the function as well as ``ARGV0``, ``ARGV1``, ``ARGV2``, ...  which
-will have the actual values of the arguments passed in.  This facilitates
-creating functions with optional arguments.
+``ARGC``
+  The number of arguments passed into the function.
 
-Furthermore, ``ARGV`` holds the list of all arguments given to the
-function and ``ARGN`` holds the list of arguments past the last expected
-argument.  Referencing to ``ARGV#`` arguments beyond ``ARGC`` have
-undefined behavior.  Checking that ``ARGC`` is greater than ``#`` is
-the only way to ensure that ``ARGV#`` was passed to the function as an
-extra argument.
+``ARGV``
+  The list of all arguments given to the function.
+
+``ARGV#``
+  The variables ``ARGV0``, ``ARGV1``, ``ARGV2``, ...  which will have the
+  actual values of the arguments passed in. References to ``ARGV#``
+  arguments beyond ``ARGC`` have undefined behavior.  Checking that
+  ``ARGC`` is greater than ``#`` is the only way to ensure that ``ARGV#``
+  was passed to the function as an extra argument.
+
+``ARGN``
+  The list of arguments past the last expected argument.
+
+``ARGNC``
+  .. versionadded:: 4.5
+
+  The number of arguments past the last expected argument.
 
 See Also
 ^^^^^^^^
diff --git a/Help/command/install.rst b/Help/command/install.rst
index 75016b0..cb8db41 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -1217,7 +1217,8 @@
             [PROJECT <project-name>|NO_PROJECT_METADATA]
             [DESTINATION <dir>]
             [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
-            [LICENSE <license-string>]
+            [DATA_LICENSE <license-string>]
+            [DEFAULT_LICENSE <license-string>]
             [DESCRIPTION <description-string>]
             [HOMEPAGE_URL <url-string>]
             [PACKAGE_URL <url-string>]
@@ -1260,16 +1261,21 @@
 
     An informational canonical package URL for the project.
 
-  ``LICENSE <license-string>``
+  ``DATA_LICENSE <license-string>``
 
     A |SPDX|_ (SPDX) `License Expression`_ that describes the license(s) of the
-    project as a whole, including documentation, resources, or other materials
-    distributed with the project, in addition to software artifacts.  See the
-    SPDX `License List`_ for a list of commonly used licenses and their
-    identifiers.
+    SBOM data itself in the generated document.  See the SPDX `License List`_
+    for a list of commonly used licenses and their identifiers. In the SPDX
+    format, it corresponds to the `dataLicense`_ property.
 
     The license of individual components is taken from the
-    :prop_tgt:`SPDX_LICENSE` property of their respective targets.
+    :prop_tgt:`SPDX_LICENSE` property of their respective targets if set and
+    the ``DEFAULT_LICENSE`` otherwise.
+
+  ``DEFAULT_LICENSE <license-string>``
+
+    A |SPDX|_ (SPDX) `License Expression`_ that describes the license(s) of any
+    components which do not otherwise specify their license(s).
 
   ``DESCRIPTION <description-string>``
 
@@ -1420,5 +1426,6 @@
 
 .. _License Expression: https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/
 .. _License List: https://spdx.org/licenses/
+.. _dataLicense: https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Properties/dataLicense/
 
 .. |SBOM| replace:: Software Bill of Material
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 60955f1..6a3b7f7 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -109,7 +109,7 @@
 set
 
 * variable ``CMAKE_EXPERIMENTAL_GENERATE_SBOM`` to
-* value ``2d856d6d-53e8-488b-a17f-d486d2cac317``.
+* value ``248471c2-d905-4c9e-81b5-b89cd27965e1``.
 
 This UUID may change in future versions of CMake.  Be sure to use the value
 documented here by the source tree of the version of CMake with which you are
diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst
index 33f81cb..3ff3090 100644
--- a/Help/manual/cmake-instrumentation.7.rst
+++ b/Help/manual/cmake-instrumentation.7.rst
@@ -260,8 +260,9 @@
   for the most recent minor version, even if an earlier minor version is
   requested.
 
-  Currently, the only supported version is ``1.0``. Query files with an unknown
-  data version will be ignored.
+  Currently, the only supported major version is ``1``.  Query files
+  requesting a minor version up to the maximum described in `Data Version`_
+  are accepted; those with an unknown data version will be ignored.
 
 ``callbacks``
   A list of command-line strings for `Callbacks`_ to handle collected
@@ -437,8 +438,8 @@
 ``<role>-<hash>-<timestamp>.json`` and contain the following data:
 
   ``version``
-    The `Data Version`_ of the snippet file. Currently the version is
-    always ``{ "major": 1, "minor": 0 }``.
+    The `Data Version`_ of the snippet file, written as
+    ``{ "major": 1, "minor": N }`` where ``N`` is the most recent minor version.
 
   ``command``
     The full command executed. Excluded when ``role`` is ``build``.
@@ -624,8 +625,8 @@
 `Callbacks`_ are executed.
 
 ``version``
-  The `Data Version`_ of the index file. Currently this is always written as:
-  ``{ "major": 1, "minor": 0 }``.
+  The `Data Version`_ of the index file, written as
+  ``{ "major": 1, "minor": N }`` where ``N`` is the most recent minor version.
 
 ``buildDir``
   The build directory of the CMake project.
@@ -727,8 +728,8 @@
 Each CMake content file contains the following:
 
   ``version``
-    The `Data Version`_ of the content file. Currently the version is
-    always ``{ "major": 1, "minor": 0 }``.
+    The `Data Version`_ of the content file, written as
+    ``{ "major": 1, "minor": N }`` where ``N`` is the most recent minor version.
 
   ``project``
     The value of :variable:`CMAKE_PROJECT_NAME`.
diff --git a/Help/prop_tgt/Fortran_BUILDING_INTRINSIC_MODULES.rst b/Help/prop_tgt/Fortran_BUILDING_INTRINSIC_MODULES.rst
index 8e0f71b..f7152ab 100644
--- a/Help/prop_tgt/Fortran_BUILDING_INTRINSIC_MODULES.rst
+++ b/Help/prop_tgt/Fortran_BUILDING_INTRINSIC_MODULES.rst
@@ -14,3 +14,7 @@
 Fortran intrinsic modules whereas turning the property off will ignore
 Fortran intrinsic modules in the dependency graph as they are supplied
 by the compiler itself.
+
+.. versionadded:: 4.5
+  This property is now honored by the :ref:`Ninja Generators`.
+  Previously it was only honored by the :ref:`Makefile Generators`.
diff --git a/Help/release/dev/file-ARCHIVE_CREATE-patterns-exclude.rst b/Help/release/dev/file-ARCHIVE_CREATE-patterns-exclude.rst
new file mode 100644
index 0000000..40cc6f0
--- /dev/null
+++ b/Help/release/dev/file-ARCHIVE_CREATE-patterns-exclude.rst
@@ -0,0 +1,6 @@
+file-ARCHIVE_CREATE-patterns-exclude
+------------------------------------
+
+* The :command:`file(ARCHIVE_CREATE)` command gained a ``PATTERNS_EXCLUDE``
+  option to omit files and directories matching the given patterns from the
+  created archive.
diff --git a/Help/release/dev/fortran-intrinsic-modules-ninja.rst b/Help/release/dev/fortran-intrinsic-modules-ninja.rst
new file mode 100644
index 0000000..b4209fa
--- /dev/null
+++ b/Help/release/dev/fortran-intrinsic-modules-ninja.rst
@@ -0,0 +1,6 @@
+fortran-intrinsic-modules-ninja
+-------------------------------
+
+* The :prop_tgt:`Fortran_BUILDING_INTRINSIC_MODULES` target property is now
+  honored by the :ref:`Ninja Generators`.  Previously it was only honored
+  by the :ref:`Makefile Generators`.
diff --git a/Help/release/dev/function-argnc.rst b/Help/release/dev/function-argnc.rst
new file mode 100644
index 0000000..94210e0
--- /dev/null
+++ b/Help/release/dev/function-argnc.rst
@@ -0,0 +1,5 @@
+function-argnc
+--------------
+
+* The :command:`function` command now sets the ``ARGNC`` variable, which
+  holds the number of arguments past the last expected argument.
diff --git a/Modules/FindRuby.cmake b/Modules/FindRuby.cmake
index 863f70a..6f865da 100644
--- a/Modules/FindRuby.cmake
+++ b/Modules/FindRuby.cmake
@@ -407,7 +407,7 @@
   _RUBY_CONFIG_VAR("sitelibdir" Ruby_SITELIB_DIR)
 
   # vendor_ruby - TODO - Not relevant and should be removed.
-  execute_process(COMMAND "${Ruby_EXECUTABLE}" -r vendor-specific -e "print 'true'"
+  execute_process(COMMAND ${Ruby_EXECUTABLE} -r rbconfig -e "print 'true' unless RbConfig::CONFIG['vendorarchdir'].nil?"
                   OUTPUT_VARIABLE Ruby_HAS_VENDOR_RUBY ERROR_QUIET)
 
   if (Ruby_HAS_VENDOR_RUBY)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index bf160c5..6973989 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 4)
 set(CMake_VERSION_MINOR 4)
-set(CMake_VERSION_PATCH 20260617)
+set(CMake_VERSION_PATCH 20260622)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx
index 6b465c1..74089e5 100644
--- a/Source/CursesDialog/cmCursesMainForm.cxx
+++ b/Source/CursesDialog/cmCursesMainForm.cxx
@@ -314,9 +314,12 @@
     char secondLine[512] = "";
     char thirdLine[512] = "";
     if (process) {
-      memset(firstLine, ' ', 68);
-      memset(secondLine, ' ', 68);
-      memset(thirdLine, ' ', 68);
+      std::fill_n(firstLine, 68, ' ');
+      firstLine[68] = '\0';
+      std::fill_n(secondLine, 68, ' ');
+      secondLine[68] = '\0';
+      std::fill_n(thirdLine, 68, ' ');
+      thirdLine[68] = '\0';
     } else {
       if (this->OkToGenerate) {
         snprintf(firstLine, sizeof(firstLine),
@@ -340,7 +343,8 @@
     move(y - 4, 0);
     char fmt[512] = "Keys: [enter] Edit an entry [d] Delete an entry";
     if (process) {
-      memset(fmt, ' ', 57);
+      std::fill_n(fmt, 57, ' ');
+      fmt[57] = '\0';
     }
     printw(fmt_s, fmt);
     move(y - 3, 0);
diff --git a/Source/bindexplib.cxx b/Source/bindexplib.cxx
index 59680aa..b43ac0f 100644
--- a/Source/bindexplib.cxx
+++ b/Source/bindexplib.cxx
@@ -391,7 +391,7 @@
               line.c_str());
       return false;
     }
-    if (line.size() < sym_end + 1) {
+    if (line.size() < sym_end) {
       fprintf(stderr, "Couldn't parse llvm-nm output line: %s\n",
               line.c_str());
       return false;
diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx
index 374e476..af92ca3 100644
--- a/Source/cmArchiveWrite.cxx
+++ b/Source/cmArchiveWrite.cxx
@@ -406,8 +406,44 @@
   return true;
 }
 
+bool cmArchiveWrite::SetExcludePatterns(
+  std::vector<std::string> const& patterns)
+{
+  if (patterns.empty()) {
+    return true;
+  }
+  if (!this->MatchObject) {
+    this->MatchObject = archive_match_new();
+    if (!this->MatchObject) {
+      this->Error = "archive_match_new: out of memory";
+      return false;
+    }
+  }
+  // NOLINTNEXTLINE(readability-use-anyofallof)
+  for (std::string const& pattern : patterns) {
+    // Reject empty patterns ourselves.  libarchive would reject them too, but
+    // only after allocating an error message on the match object that
+    // archive_match_free() does not release.
+    if (pattern.empty()) {
+      this->Error = "exclusion pattern must not be empty";
+      return false;
+    }
+    if (archive_match_exclude_pattern(this->MatchObject, pattern.c_str()) !=
+        ARCHIVE_OK) {
+      this->Error =
+        cmStrCat("Failed to add to exclusion list: ", pattern, " (",
+                 cm_archive_error_string(this->MatchObject), ')');
+      return false;
+    }
+  }
+  return true;
+}
+
 cmArchiveWrite::~cmArchiveWrite()
 {
+  if (this->MatchObject) {
+    archive_match_free(this->MatchObject);
+  }
   archive_read_free(this->Disk);
   archive_write_free(this->Archive);
 }
@@ -425,6 +461,23 @@
 bool cmArchiveWrite::AddPath(std::string const& path, size_t skip,
                              char const* prefix, bool recursive)
 {
+  // Skip paths whose archive entry name matches an exclusion pattern.  For a
+  // directory this also prevents descending into it, pruning the subtree.
+  if (this->MatchObject && skip < path.length()) {
+    cm::string_view out = cm::string_view(path).substr(skip);
+    std::string dest = cmStrCat(prefix ? prefix : "", out);
+    Entry e;
+    cm_archive_entry_copy_pathname(e, dest.c_str());
+    int matched = archive_match_path_excluded(this->MatchObject, e);
+    if (matched < 0) {
+      this->Error = cmStrCat("archive_match_path_excluded: ",
+                             cm_archive_error_string(this->MatchObject));
+      return false;
+    }
+    if (matched > 0) {
+      return true;
+    }
+  }
   if (path != "." || (this->Format != "zip" && this->Format != "7zip")) {
     if (!this->AddFile(path, skip, prefix)) {
       return false;
diff --git a/Source/cmArchiveWrite.h b/Source/cmArchiveWrite.h
index dfefc72..00c8ebc 100644
--- a/Source/cmArchiveWrite.h
+++ b/Source/cmArchiveWrite.h
@@ -7,6 +7,7 @@
 #include <cstddef>
 #include <iosfwd>
 #include <string>
+#include <vector>
 
 #if defined(CMAKE_BOOTSTRAP)
 #  error "cmArchiveWrite not allowed during bootstrap build!"
@@ -143,6 +144,11 @@
     this->Gname = "";
   }
 
+  //! Sets exclusion patterns.  Any path whose archive entry name matches one
+  //! of the patterns is skipped, and excluded directories are not descended
+  //! into.  Returns false and sets the error if a pattern cannot be added.
+  bool SetExcludePatterns(std::vector<std::string> const& patterns);
+
 private:
   bool Okay() const { return this->Error.empty(); }
   bool AddPath(std::string const& path, size_t skip, char const* prefix,
@@ -158,6 +164,7 @@
   std::ostream& Stream;
   struct archive* Archive;
   struct archive* Disk;
+  struct archive* MatchObject = nullptr;
   bool Verbose = false;
   std::string Format;
   std::string Error;
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index e3508b5..7b15c97 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -1341,8 +1341,9 @@
   std::vector<std::string> files;
   files.push_back(file);
 
-  if (!cmSystemTools::CreateTar(
-        tarFile, files, {}, cmSystemTools::TarCompressGZip, "UTF-8", false)) {
+  if (!cmSystemTools::CreateTar(tarFile, files, {}, {},
+                                cmSystemTools::TarCompressGZip, "UTF-8",
+                                false)) {
     cmCTestLog(this, ERROR_MESSAGE,
                "Error creating tar while "
                "encoding file: "
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx
index 2d9232c..3d50bf1 100644
--- a/Source/cmExperimental.cxx
+++ b/Source/cmExperimental.cxx
@@ -55,7 +55,7 @@
     {},
     cmExperimental::TryCompileCondition::Never },
   { "GenerateSbom",
-    "2d856d6d-53e8-488b-a17f-d486d2cac317",
+    "248471c2-d905-4c9e-81b5-b89cd27965e1",
     "CMAKE_EXPERIMENTAL_GENERATE_SBOM",
     "CMake's support for generating software bill of materials (Sbom) "
     "information in SPDX format is experimental. It is meant only for "
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 222acd7..fb82c3c 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -3720,6 +3720,7 @@
     bool Verbose = false;
     // "PATHS" requires at least one value, but use a custom check below.
     ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PatternsExclude;
   };
 
   static auto const parser =
@@ -3733,7 +3734,8 @@
       .Bind("THREADS"_s, &Arguments::Threads)
       .Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory)
       .Bind("VERBOSE"_s, &Arguments::Verbose)
-      .Bind("PATHS"_s, &Arguments::Paths);
+      .Bind("PATHS"_s, &Arguments::Paths)
+      .Bind("PATTERNS_EXCLUDE"_s, &Arguments::PatternsExclude);
 
   std::vector<std::string> unrecognizedArguments;
   auto parsedArgs =
@@ -3866,9 +3868,10 @@
   }
 
   if (!cmSystemTools::CreateTar(
-        parsedArgs.Output, parsedArgs.Paths, parsedArgs.WorkingDirectory,
-        compress, parsedArgs.Encoding, parsedArgs.Verbose, parsedArgs.MTime,
-        parsedArgs.Format, compressionLevel, threads)) {
+        parsedArgs.Output, parsedArgs.Paths, parsedArgs.PatternsExclude,
+        parsedArgs.WorkingDirectory, compress, parsedArgs.Encoding,
+        parsedArgs.Verbose, parsedArgs.MTime, parsedArgs.Format,
+        compressionLevel, threads)) {
     status.SetError(cmStrCat("failed to compress: ", parsedArgs.Output));
     cmSystemTools::SetFatalErrorOccurred();
     return false;
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index 3e62649..0d02993 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -25,7 +25,7 @@
 
 namespace {
 std::string const ARGC = "ARGC";
-std::string const kFUNCTION_ARGNC = "_FUNCTION_ARGNC";
+std::string const ARGNC = "ARGNC";
 std::string const ARGN = "ARGN";
 std::string const ARGV = "ARGV";
 std::string const CMAKE_CURRENT_FUNCTION = "CMAKE_CURRENT_FUNCTION";
@@ -94,7 +94,7 @@
     makefile.AddDefinition(this->Args[j], expandedArgs[j - 1]);
   }
 
-  // define ARGV, ARGN, and _FUNCTION_ARGNC
+  // define ARGV, ARGN, and ARGNC
   auto const argvDef = cmList::to_string(expandedArgs);
   auto const expIt = expandedArgs.begin() + (this->Args.size() - 1);
   auto const argnDef =
@@ -105,8 +105,8 @@
   makefile.MarkVariableAsUsed(ARGV);
   makefile.AddDefinition(ARGN, argnDef);
   makefile.MarkVariableAsUsed(ARGN);
-  makefile.AddDefinition(kFUNCTION_ARGNC, functionArgncDef);
-  makefile.MarkVariableAsUsed(kFUNCTION_ARGNC);
+  makefile.AddDefinition(ARGNC, functionArgncDef);
+  makefile.MarkVariableAsUsed(ARGNC);
 
   makefile.AddDefinition(CMAKE_CURRENT_FUNCTION, this->Args.front());
   makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION);
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 8d29ec4..bed1634 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -7,6 +7,7 @@
 #include <cstdio>
 #include <functional>
 #include <iterator>
+#include <set>
 #include <sstream>
 #include <utility>
 
@@ -2565,6 +2566,7 @@
   std::vector<std::string> includes;
   std::string dir_top_bld;
   std::string module_dir;
+  bool building_intrinsics = false;
 
   if (!arg_src_orig.empty()) {
     // Prepend the original source file's directory as an include directory
@@ -2615,6 +2617,10 @@
 
     Json::Value const& tdi_submodule_ext = tdi["submodule-ext"];
     fc.SModExt = tdi_submodule_ext.asString();
+
+    Json::Value const& tdi_building_intrinsics =
+      tdi["building-intrinsic-modules"];
+    building_intrinsics = tdi_building_intrinsics.asBool();
   }
 
   cmFortranSourceInfo finfo;
@@ -2643,7 +2649,11 @@
     }
     info->ScanDep.Provides.emplace_back(src_info);
   }
-  for (std::string const& require : finfo.Requires) {
+  std::set<std::string> requiredModules = finfo.Requires;
+  if (building_intrinsics) {
+    requiredModules.insert(finfo.Intrinsics.begin(), finfo.Intrinsics.end());
+  }
+  for (std::string const& require : requiredModules) {
     // Require modules not provided in the same source.
     if (finfo.Provides.count(require)) {
       continue;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 0215e34..4da4569 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -3583,7 +3583,10 @@
   target->AddAttribute("name", this->CreateString(gtgt->GetName()));
   target->AddAttribute("productName", this->CreateString(gtgt->GetName()));
 
-  cmXCodeObject* fileRef = this->CreateObject(cmXCodeObject::PBXFileReference);
+  cmXCodeObject* fileRef =
+    this->CreateObject(cmXCodeObject::PBXFileReference,
+                       cmStrCat("PBXFileReference:product:", gtgt->GetName(),
+                                ':', targetBinaryPath));
   if (char const* fileType = this->GetTargetFileType(gtgt)) {
     fileRef->AddAttribute("explicitFileType", this->CreateString(fileType));
   }
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index f2505de..ec5c369 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -2376,6 +2376,8 @@
       this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_SEP");
     tdi["submodule-ext"] =
       this->Makefile->GetSafeDefinition("CMAKE_Fortran_SUBMODULE_EXT");
+    tdi["building-intrinsic-modules"] =
+      this->GeneratorTarget->IsFortranBuildingIntrinsicModules();
   } else if (lang == "CXX") {
     // No extra information necessary.
   }
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 88b6295..19f23ee 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -182,12 +182,11 @@
     }
     parseFromArg = true;
     argIter++; // move past PARSE_ARGN
-    std::string argncStr =
-      status.GetMakefile().GetSafeDefinition("_FUNCTION_ARGNC");
+    std::string argncStr = status.GetMakefile().GetSafeDefinition("ARGNC");
     if (!cmStrToULong(argncStr, &argnc)) {
       status.GetMakefile().IssueMessage(
         MessageType::FATAL_ERROR,
-        cmStrCat("PARSE_ARGN called with _FUNCTION_ARGNC='", argncStr,
+        cmStrCat("PARSE_ARGN called with ARGNC='", argncStr,
                  "' that is not an unsigned integer"));
       cmSystemTools::SetFatalErrorOccurred();
       return true;
diff --git a/Source/cmSbomArguments.cxx b/Source/cmSbomArguments.cxx
index 155aefd..b5c48fa 100644
--- a/Source/cmSbomArguments.cxx
+++ b/Source/cmSbomArguments.cxx
@@ -65,6 +65,14 @@
       return false;
     }
   }
+
+  if (!this->License.empty()) {
+    // Do not allow the license argument to be provided as SBOM only accepts a
+    // DEFAULT_LICENSE
+    status.SetError("SBOM given unknown argument: \"LICENSE\".");
+    return false;
+  }
+
   return true;
 }
 
diff --git a/Source/cmSbomArguments.h b/Source/cmSbomArguments.h
index ab62226..a45d52e 100644
--- a/Source/cmSbomArguments.h
+++ b/Source/cmSbomArguments.h
@@ -41,6 +41,8 @@
 
   ArgumentParser::NonEmpty<std::string> Format;
   ArgumentParser::NonEmpty<std::string> PackageUrl;
+  ArgumentParser::NonEmpty<std::string> DefaultLicense;
+  ArgumentParser::NonEmpty<std::string> DataLicense;
 
 protected:
   cm::string_view CommandName() const override;
@@ -55,6 +57,8 @@
     Bind(base, parser, "SBOM"_s, &cmProjectInfoArguments::PackageName);
     Bind(self, parser, "FORMAT"_s, &cmSbomArguments::Format);
     Bind(self, parser, "PACKAGE_URL"_s, &cmSbomArguments::PackageUrl);
+    Bind(self, parser, "DEFAULT_LICENSE"_s, &cmSbomArguments::DefaultLicense);
+    Bind(self, parser, "DATA_LICENSE"_s, &cmSbomArguments::DataLicense);
     cmProjectInfoArguments::Bind(parser, self);
   }
 };
diff --git a/Source/cmSbomBuilder.cxx b/Source/cmSbomBuilder.cxx
index 16397a2..aed9b15 100644
--- a/Source/cmSbomBuilder.cxx
+++ b/Source/cmSbomBuilder.cxx
@@ -70,7 +70,8 @@
   , PackageDescription(std::move(args.Description))
   , PackageWebsite(std::move(args.Website))
   , PackageUrl(std::move(args.PackageUrl))
-  , PackageLicense(std::move(args.License))
+  , DataLicense(std::move(args.DataLicense))
+  , DefaultLicense(std::move(args.DefaultLicense))
   , PackageFormat(args.GetFormat())
 {
 }
@@ -222,8 +223,12 @@
     proj.Description = this->PackageDescription;
   }
 
-  if (!this->PackageLicense.empty()) {
-    proj.DataLicense = this->PackageLicense;
+  if (!this->DataLicense.empty()) {
+    cmSpdxLicenseExpression license;
+    license.SpdxId = cmStrCat("urn:", PackageName, "#LicenseExpression");
+    license.CreationInfo = ci;
+    license.LicenseExpression = this->DataLicense;
+    proj.DataLicense = license;
   }
 
   return proj;
@@ -319,7 +324,31 @@
           this->AddPackageInformation(pkg, pkgIt->first, pkgIt->second);
         }
       }
-      return { true, insert_back(project->Elements, std::move(pkg)) };
+
+      cmSpdxPackage const* pkgPtr =
+        insert_back(project->Elements, std::move(pkg));
+      if (!linkInfo.License.empty() &&
+          !cm::contains(this->GeneratedLinkLicenses, name)) {
+        this->GeneratedLinkLicenses.emplace(name);
+
+        cmSpdxLicenseExpression license;
+        license.SpdxId = cmStrCat("urn:", name, "#LicenseExpression");
+        license.CreationInfo = ci;
+        license.LicenseExpression = linkInfo.License;
+
+        cmSpdxRelationship relHasLicense;
+        relHasLicense.SpdxId =
+          cmStrCat("urn:", name, "#DeclaredLicenseRelationship");
+        relHasLicense.CreationInfo = ci;
+        relHasLicense.RelationshipType =
+          cmSpdxRelationship::HAS_DECLARED_LICENSE;
+        relHasLicense.From = pkgPtr;
+        relHasLicense.To.emplace_back(std::move(license));
+
+        insert_back(doc.Graph, std::move(relHasLicense));
+      }
+
+      return { true, pkgPtr };
     }
 
     cmSpdxPackage pkg;
@@ -368,10 +397,40 @@
                                          current, allTargets, config);
   status &= this->GenerateLinkProperties(
     doc, proj, ci, "INTERFACE_LINK_LIBRARIES", current, allTargets, config);
-
+  status &= this->GenerateMetaProperties(doc, proj, ci, current);
   return status;
 }
 
+bool cmSbomBuilder::GenerateMetaProperties(
+  cmSbomDocument& doc, cmSpdxDocument* /*project*/,
+  cmSpdxCreationInfo const* ci, TargetProperties const& current) const
+{
+  std::string licenseExpr = this->DefaultLicense;
+  cmValue licenseExprProp = current.Target->GetProperty("SPDX_LICENSE");
+  if (licenseExprProp) {
+    licenseExpr = licenseExprProp;
+  }
+  if (!licenseExpr.empty()) {
+    auto const& tgtName = current.Target->GetName();
+
+    cmSpdxLicenseExpression license;
+    license.SpdxId = cmStrCat("urn:", tgtName, "#LicenseExpression");
+    license.CreationInfo = ci;
+    license.LicenseExpression = std::move(licenseExpr);
+
+    cmSpdxRelationship relHasLicense;
+    relHasLicense.SpdxId =
+      cmStrCat("urn:", tgtName, "#DeclaredLicenseRelationship");
+    relHasLicense.CreationInfo = ci;
+    relHasLicense.RelationshipType = cmSpdxRelationship::HAS_DECLARED_LICENSE;
+    relHasLicense.From = current.Package;
+    relHasLicense.To.emplace_back(std::move(license));
+
+    insert_back(doc.Graph, std::move(relHasLicense));
+  }
+  return true;
+}
+
 bool cmSbomBuilder::PopulateLinkLibrariesProperty(
   cmGeneratorTarget const* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,
@@ -428,9 +487,12 @@
                                      std::string const& linkedName,
                                      cmGeneratorTarget const* linkedTarget)
 {
+  auto linkedLicense = linkedTarget->GetSafeProperty("SPDX_LICENSE");
+
   if (cm::contains(this->SbomTargets, linkedTarget)) {
-    this->LinkTargets.emplace(linkedName,
-                              LinkInfo{ "", linkedTarget->GetExportName() });
+    this->LinkTargets.emplace(
+      linkedName,
+      LinkInfo{ "", linkedTarget->GetExportName(), linkedLicense });
     return true;
   }
 
@@ -468,7 +530,8 @@
     } else {
       component = linkedName.substr(prefix.length());
     }
-    this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component });
+    this->LinkTargets.emplace(linkedName,
+                              LinkInfo{ pkgName, component, linkedLicense });
     cmPackageInformation& req =
       this->Requirements.insert(std::move(*pkgInfo)).first->second;
     req.Components.emplace(std::move(component));
@@ -494,9 +557,11 @@
     std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
     std::string component = linkedTarget->GetExportName();
     if (pkgName == this->GetPackageName()) {
-      this->LinkTargets.emplace(linkedName, LinkInfo{ "", component });
+      this->LinkTargets.emplace(linkedName,
+                                LinkInfo{ "", component, linkedLicense });
     } else {
-      this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component });
+      this->LinkTargets.emplace(linkedName,
+                                LinkInfo{ pkgName, component, linkedLicense });
       this->Requirements[pkgName].Components.emplace(std::move(component));
     }
     return true;
@@ -535,7 +600,8 @@
           "\" (first alphabetically)."));
     }
     std::string component = linkedTarget->GetExportName();
-    this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component });
+    this->LinkTargets.emplace(linkedName,
+                              LinkInfo{ pkgName, component, linkedLicense });
     this->Requirements[pkgName].Components.emplace(std::move(component));
     return true;
   }
diff --git a/Source/cmSbomBuilder.h b/Source/cmSbomBuilder.h
index 961e9db..f092e3f 100644
--- a/Source/cmSbomBuilder.h
+++ b/Source/cmSbomBuilder.h
@@ -129,6 +129,9 @@
                               TargetProperties const& current,
                               std::vector<TargetProperties> const& allTargets,
                               std::string const& config) const;
+  bool GenerateMetaProperties(cmSbomDocument& doc, cmSpdxDocument* project,
+                              cmSpdxCreationInfo const* ci,
+                              TargetProperties const& current) const;
 
   bool NoteLinkedTarget(cmGeneratorTarget const* target,
                         std::string const& linkedName,
@@ -163,6 +166,7 @@
   {
     std::string Package;
     std::string Component;
+    std::string License;
   };
 
   // Metadata
@@ -172,7 +176,8 @@
   std::string const PackageDescription;
   std::string const PackageWebsite;
   std::string const PackageUrl;
-  std::string const PackageLicense;
+  std::string const DataLicense;
+  std::string const DefaultLicense;
   cmSbomArguments::SbomFormat const PackageFormat;
 
   // Derived from inputs at generate time
@@ -182,4 +187,7 @@
   std::map<std::string, LinkInfo> LinkTargets;
   std::map<std::string, cmPackageInformation> Requirements;
   std::vector<std::string> Configurations;
+
+  // Targets for which license data has been generated
+  mutable std::set<std::string> GeneratedLinkLicenses;
 };
diff --git a/Source/cmSpdx.cxx b/Source/cmSpdx.cxx
index 870eec4..492c9a3 100644
--- a/Source/cmSpdx.cxx
+++ b/Source/cmSpdx.cxx
@@ -128,6 +128,8 @@
       return "contains";
     case cmSpdxRelationship::RelationshipTypeId::DEPENDS_ON:
       return "dependsOn";
+    case cmSpdxRelationship::RelationshipTypeId::HAS_DECLARED_LICENSE:
+      return "hasDeclaredLicense";
     case cmSpdxRelationship::RelationshipTypeId::OTHER:
       return "other";
   }
@@ -497,6 +499,20 @@
   }
 }
 
+void cmSpdxAnyLicenseInfo::Serialize(cmSbomSerializer& serializer) const
+{
+  cmSpdxElement::Serialize(serializer);
+  serializer.AddString("type", "simplelicensing_AnyLicenseInfo");
+}
+
+void cmSpdxLicenseExpression::Serialize(cmSbomSerializer& serializer) const
+{
+  cmSpdxAnyLicenseInfo::Serialize(serializer);
+  serializer.AddString("type", "simplelicensing_LicenseExpression");
+  SerializeIfPresent(serializer, "simplelicensing_licenseExpression",
+                     LicenseExpression);
+}
+
 void cmSpdxDocument::Serialize(cmSbomSerializer& serializer) const
 {
   cmSpdxElementCollection::Serialize(serializer);
@@ -508,7 +524,9 @@
   if (NamespaceMap) {
     serializer.AddVisitable("namespaceMap", *NamespaceMap);
   }
-  SerializeIfPresent(serializer, "dataLicense", DataLicense);
+  if (DataLicense) {
+    serializer.AddVisitable("dataLicense", *DataLicense);
+  }
 }
 
 void cmSbomDocument::Serialize(cmSbomSerializer& serializer) const
diff --git a/Source/cmSpdx.h b/Source/cmSpdx.h
index adaa26a..f792e7a 100644
--- a/Source/cmSpdx.h
+++ b/Source/cmSpdx.h
@@ -159,6 +159,7 @@
     DESCRIBES,
     CONTAINS,
     DEPENDS_ON,
+    HAS_DECLARED_LICENSE,
     OTHER
   };
 
@@ -343,11 +344,23 @@
   void Serialize(cmSbomSerializer&) const override;
 };
 
+struct cmSpdxAnyLicenseInfo : cmSpdxElement
+{
+  void Serialize(cmSbomSerializer&) const override;
+};
+
+struct cmSpdxLicenseExpression final : cmSpdxAnyLicenseInfo
+{
+  cm::optional<std::string> LicenseExpression;
+
+  void Serialize(cmSbomSerializer&) const override;
+};
+
 struct cmSpdxDocument final : cmSpdxElementCollection
 {
   cm::optional<cmSbomObject> ExternalMap;
   cm::optional<cmSbomObject> NamespaceMap;
-  std::string DataLicense;
+  cm::optional<cmSbomObject> DataLicense;
 
   void Serialize(cmSbomSerializer& serializer) const override;
 };
diff --git a/Source/cmStringReplaceHelper.cxx b/Source/cmStringReplaceHelper.cxx
index 0e18457..aa00907 100644
--- a/Source/cmStringReplaceHelper.cxx
+++ b/Source/cmStringReplaceHelper.cxx
@@ -93,7 +93,7 @@
       this->Replacements.emplace_back(
         this->ReplaceExpression.substr(l, r - l));
     } else {
-      if (r - l > 0) {
+      if (r > l) {
         this->Replacements.emplace_back(
           this->ReplaceExpression.substr(l, r - l));
       }
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 7976757..ea87266 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -2238,6 +2238,7 @@
 
 bool cmSystemTools::CreateTar(
   std::string const& arFileName, std::vector<std::string> const& files,
+  std::vector<std::string> const& excludeFiles,
   std::string const& workingDirectory, cmTarCompression compressType,
   std::string const& encoding, bool verbose, std::string const& mtime,
   std::string const& format, int compressionLevel, int numThreads)
@@ -2300,6 +2301,10 @@
   }
   a.SetMTime(mtime);
   a.SetVerbose(verbose);
+  if (!a.SetExcludePatterns(excludeFiles)) {
+    cmSystemTools::Error(a.GetError());
+    return false;
+  }
   bool tarCreatedSuccessfully = true;
   for (auto path : files) {
     if (cmSystemTools::FileIsFullPath(path)) {
@@ -2315,6 +2320,7 @@
 #else
   (void)arFileName;
   (void)files;
+  (void)excludeFiles;
   (void)encoding;
   (void)verbose;
   return false;
@@ -3889,7 +3895,8 @@
       // There is no RPATH or RUNPATH anyway.
       return true;
     }
-    if (se_count == 2 && se[1]->IndexInSection < se[0]->IndexInSection) {
+    if (se_count == 2 && se[0] && se[1] &&
+        se[1]->IndexInSection < se[0]->IndexInSection) {
       std::swap(se[0], se[1]);
     }
 
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index e72e5ea..c09ce30 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -527,6 +527,7 @@
                       std::string const& encoding, bool verbose);
   static bool CreateTar(std::string const& arFileName,
                         std::vector<std::string> const& files,
+                        std::vector<std::string> const& excludeFiles,
                         std::string const& workingDirectory,
                         cmTarCompression compressType,
                         std::string const& encoding, bool verbose,
diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx
index 960e36c..aa3975f 100644
--- a/Source/cmTargetPropCommandBase.cxx
+++ b/Source/cmTargetPropCommandBase.cxx
@@ -81,14 +81,16 @@
   }
 
   bool prepend = false;
-  if ((flags & PROCESS_BEFORE) && args[argIndex] == "BEFORE") {
+  if ((flags & PROCESS_BEFORE) && argIndex < args.size() &&
+      args[argIndex] == "BEFORE") {
     if (args.size() < 3) {
       this->SetError("called with incorrect number of arguments");
       return false;
     }
     prepend = true;
     ++argIndex;
-  } else if ((flags & PROCESS_AFTER) && args[argIndex] == "AFTER") {
+  } else if ((flags & PROCESS_AFTER) && argIndex < args.size() &&
+             args[argIndex] == "AFTER") {
     if (args.size() < 3) {
       this->SetError("called with incorrect number of arguments");
       return false;
@@ -97,7 +99,8 @@
     ++argIndex;
   }
 
-  if ((flags & PROCESS_REUSE_FROM) && args[argIndex] == "REUSE_FROM") {
+  if ((flags & PROCESS_REUSE_FROM) && argIndex < args.size() &&
+      args[argIndex] == "REUSE_FROM") {
     if (args.size() != 3) {
       this->SetError("called with incorrect number of arguments");
       return false;
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index e942370..01be8a1 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -2129,9 +2129,9 @@
         if (files.empty()) {
           std::cerr << "tar: No files or directories specified\n";
         }
-        if (!cmSystemTools::CreateTar(outFile, files, {}, compress, encoding,
-                                      verbose, mtime, format, compressionLevel,
-                                      numThreads)) {
+        if (!cmSystemTools::CreateTar(outFile, files, {}, {}, compress,
+                                      encoding, verbose, mtime, format,
+                                      compressionLevel, numThreads)) {
           cmSystemTools::Error(cmStrCat("Problem creating tar:\n  ", outFile));
           return 1;
         }
diff --git a/Tests/CMakeLib/testSpdxSerializer.cxx b/Tests/CMakeLib/testSpdxSerializer.cxx
index 7a5f6be..95e6caa 100644
--- a/Tests/CMakeLib/testSpdxSerializer.cxx
+++ b/Tests/CMakeLib/testSpdxSerializer.cxx
@@ -28,7 +28,6 @@
 
   cmSpdxDocument spdxValue;
   spdxValue.SpdxId = "_:SPDXRef-Document";
-  spdxValue.DataLicense = "CC0-1.0";
   auto spdx = insert_back(doc.Graph, std::move(spdxValue));
 
   {
diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx
index a901ab2..54a65c8 100644
--- a/Tests/CMakeLib/testUVStreambuf.cxx
+++ b/Tests/CMakeLib/testUVStreambuf.cxx
@@ -246,7 +246,8 @@
               << std::endl;
     goto end;
   }
-  if (std::memcmp(inputData.data(), outputData, 64)) {
+  if (std::memcmp(inputData.data(), outputData, //-V1086 Ignore underflow
+                  64)) {
     std::cout << "Read data does not match write data" << std::endl;
     goto end;
   }
diff --git a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake
index 78debd2..39bd58c 100644
--- a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake
+++ b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake
@@ -2,7 +2,7 @@
 
 set(common_test_options
   -Wno-author
-  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317"
+  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=248471c2-d905-4c9e-81b5-b89cd27965e1"
   "-DCMAKE_INSTALL_SBOM_FORMATS:STRING=JSON"
   "-DCMAKE_INSTALL_LIBDIR=lib"
 )
diff --git a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake
index 02b5ce3..43678f2 100644
--- a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake
+++ b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake
@@ -4,7 +4,7 @@
   SBOM test_targets
   EXPORTS test_targets
   DESCRIPTION "An eloquent description"
-  LICENSE "BSD-3"
+  DATA_LICENSE "BSD-3"
   HOMEPAGE_URL "www.example.com"
   PACKAGE_URL "https://example.com/test_targets.tar.gz"
   VERSION "1.3.4"
diff --git a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake
index 4f1f0ef..41b8a45 100644
--- a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake
@@ -2,7 +2,7 @@
 
 set(common_test_options
   -Wno-author
-  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317"
+  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=248471c2-d905-4c9e-81b5-b89cd27965e1"
 )
 
 function(run_cmake_error test)
diff --git a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake
index 4685247..17f7e61 100644
--- a/Tests/RunCMake/File_Archive/RunCMakeTest.cmake
+++ b/Tests/RunCMake/File_Archive/RunCMakeTest.cmake
@@ -72,6 +72,10 @@
 run_cmake(zip-filtered-exclude)
 run_cmake(zip-filtered-exclude-precedence)
 
+# Excluding selected files or directories from creation
+run_cmake(create-filtered-exclude)
+run_cmake(create-empty-pattern-exclude)
+
 run_cmake(create-missing-args)
 run_cmake(extract-missing-args)
 run_cmake(extract-missing-pattern)
diff --git a/Tests/RunCMake/File_Archive/create-empty-pattern-exclude-result.txt b/Tests/RunCMake/File_Archive/create-empty-pattern-exclude-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/File_Archive/create-empty-pattern-exclude-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Archive/create-empty-pattern-exclude-stderr.txt b/Tests/RunCMake/File_Archive/create-empty-pattern-exclude-stderr.txt
new file mode 100644
index 0000000..9b07333
--- /dev/null
+++ b/Tests/RunCMake/File_Archive/create-empty-pattern-exclude-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: exclusion pattern must not be empty
+CMake Error at create-empty-pattern-exclude\.cmake:[0-9]+ \(file\):
+  file failed to compress:
diff --git a/Tests/RunCMake/File_Archive/create-empty-pattern-exclude.cmake b/Tests/RunCMake/File_Archive/create-empty-pattern-exclude.cmake
new file mode 100644
index 0000000..7027705
--- /dev/null
+++ b/Tests/RunCMake/File_Archive/create-empty-pattern-exclude.cmake
@@ -0,0 +1,8 @@
+set(COMPRESS_DIR ${CMAKE_CURRENT_BINARY_DIR}/compress_dir)
+file(WRITE ${COMPRESS_DIR}/f1.txt "f1")
+
+file(ARCHIVE_CREATE
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/out.tar
+  FORMAT gnutar
+  PATHS ${COMPRESS_DIR}
+  PATTERNS_EXCLUDE "*.o" "")    # empty pattern is rejected
diff --git a/Tests/RunCMake/File_Archive/create-filtered-exclude.cmake b/Tests/RunCMake/File_Archive/create-filtered-exclude.cmake
new file mode 100644
index 0000000..e75107f
--- /dev/null
+++ b/Tests/RunCMake/File_Archive/create-filtered-exclude.cmake
@@ -0,0 +1,30 @@
+set(OUTPUT_NAME "test.zip")
+
+set(ARCHIVE_FORMAT zip)
+
+# Exclude entries matching PATTERNS_EXCLUDE while *creating* the archive.
+set(COMPRESSION_OPTIONS
+  PATTERNS_EXCLUDE
+    "compress_dir/d 2/*"   # exclude everything under a directory
+    "d-4"                  # exclude by unanchored name (dir + contents)
+    "no_such_entry"        # matches nothing: must not be an error
+)
+
+# Everything that was not excluded must still be packed and extracted.
+set(CUSTOM_CHECK_FILES
+  "f1.txt"
+  "d1/f1.txt"
+  "d + 3/f1.txt"
+  "d_4/f1.txt"                   # underscore, not the excluded "d-4"
+  "My Special Directory/f1.txt"
+)
+
+# The excluded entries must not be present in the created archive.
+set(NOT_EXISTING_FILES_CHECK
+  "d 2/f1.txt"
+  "d-4/f1.txt"
+)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)
diff --git a/Tests/RunCMake/File_Archive/roundtrip.cmake b/Tests/RunCMake/File_Archive/roundtrip.cmake
index bdbc600..a0611c9 100644
--- a/Tests/RunCMake/File_Archive/roundtrip.cmake
+++ b/Tests/RunCMake/File_Archive/roundtrip.cmake
@@ -65,6 +65,7 @@
   COMPRESSION "${COMPRESSION_TYPE}"
   WORKING_DIRECTORY "${WORKING_DIRECTORY}"
   ${ENCODING_OPTIONS}
+  ${COMPRESSION_OPTIONS}
   VERBOSE
   PATHS ${FULL_COMPRESS_DIR})
 
diff --git a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake
index daaf3b4..d1f99b7 100644
--- a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake
+++ b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake
@@ -3,7 +3,7 @@
 install(
   SBOM test_targets
   DESCRIPTION "An eloquent description"
-  LICENSE "BSD-3"
+  DATA_LICENSE "BSD-3"
   HOMEPAGE_URL "www.example.com"
   PACKAGE_URL "https://example.com/test_targets.tar.gz"
   VERSION "1.3.4"
diff --git a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake
index fd567a8..bc1bddc 100644
--- a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake
+++ b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake
@@ -2,7 +2,7 @@
 
 set(common_test_options
   -Wno-author
-  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317"
+  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=248471c2-d905-4c9e-81b5-b89cd27965e1"
 )
 
 function(run_cmake_error test)
diff --git a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake
index 697eb70..83ffda6 100644
--- a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake
+++ b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake
@@ -36,6 +36,25 @@
 }
 ]=])
 
+set(LICENSE_EXPECTED [=[
+{
+  "creationInfo" : "_:Build#CreationInfo",
+  "from" : "urn:test#Package",
+  "relationshipType" : "hasDeclaredLicense",
+  "spdxId" : "urn:test#DeclaredLicenseRelationship",
+  "to" :
+  [
+    {
+      "creationInfo" : "_:Build#CreationInfo",
+      "simplelicensing_licenseExpression" : "license-for-test-target",
+      "spdxId" : "urn:test#LicenseExpression",
+      "type" : "simplelicensing_LicenseExpression"
+    }
+  ],
+  "type" : "Relationship"
+}
+]=])
+
 
 expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
 string(JSON CREATION_INFO GET "${content}" "@graph" "0")
@@ -44,3 +63,6 @@
 string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1")
 expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED)
 expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement")
+
+string(JSON LICENSE_RELATIONSHIP GET "${content}" "@graph" "2")
+expect_object("${LICENSE_RELATIONSHIP}" LICENSE_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/ProjectMetadata.cmake b/Tests/RunCMake/Sbom/ProjectMetadata.cmake
index 02604fc..c56c0e2 100644
--- a/Tests/RunCMake/Sbom/ProjectMetadata.cmake
+++ b/Tests/RunCMake/Sbom/ProjectMetadata.cmake
@@ -10,6 +10,7 @@
   VERSION 1.2.0)
 
 add_library(test INTERFACE)
+set_target_properties(test PROPERTIES SPDX_LICENSE license-for-test-target)
 
 install(
   TARGETS test
diff --git a/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake b/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake
index 33fca83..6d3247c 100644
--- a/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake
+++ b/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake
@@ -3,7 +3,13 @@
 set(DOCUMENT_METADATA_EXPECTED [=[
 {
   "description" : "An eloquent description",
-  "dataLicense" : "BSD-3"
+  "dataLicense" :
+  {
+    "creationInfo" : "_:Build#CreationInfo",
+    "simplelicensing_licenseExpression" : "BSD-3",
+    "spdxId" : "urn:test_targets#LicenseExpression",
+    "type" : "simplelicensing_LicenseExpression"
+  }
 }
 ]=])
 
diff --git a/Tests/RunCMake/Sbom/Requirements-install-check.cmake b/Tests/RunCMake/Sbom/Requirements-install-check.cmake
index 92f2010..5e5aa14 100644
--- a/Tests/RunCMake/Sbom/Requirements-install-check.cmake
+++ b/Tests/RunCMake/Sbom/Requirements-install-check.cmake
@@ -119,6 +119,24 @@
 }
 ]=])
 
+set(TEST_LIBA_LINKED_LICENSE_EXPECTED [=[
+{
+  "creationInfo" : "_:Build#CreationInfo",
+  "from" : "urn:test:liba#Package",
+  "relationshipType" : "hasDeclaredLicense",
+  "spdxId" : "urn:test::liba#DeclaredLicenseRelationship",
+  "to" :
+  [
+    {
+      "creationInfo" : "_:Build#CreationInfo",
+      "simplelicensing_licenseExpression" : "license-for-test-liba",
+      "spdxId" : "urn:test::liba#LicenseExpression",
+      "type" : "simplelicensing_LicenseExpression"
+    }
+  ],
+  "type" : "Relationship"
+}
+]=])
 
 set(CREATION_INFO_EXPECTED [=[
 {
@@ -147,3 +165,7 @@
 expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBD "rootElement")
 expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_TEST "element")
 expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element")
+
+# Check that the license target property imported from cmake config is reported
+string(JSON LICENSE_RELATIONSHIP GET "${BAR_CONTENT}" "@graph" "2")
+expect_object("${LICENSE_RELATIONSHIP}" TEST_LIBA_LINKED_LICENSE_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake
index 7114364..1c698e8 100644
--- a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake
+++ b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake
@@ -67,6 +67,25 @@
 }
 ]=])
 
+set(LICENSE_EXPECTED [=[
+{
+  "creationInfo" : "_:Build#CreationInfo",
+  "from" : "urn:foo:foo#Package",
+  "relationshipType" : "hasDeclaredLicense",
+  "spdxId" : "urn:foo::foo#DeclaredLicenseRelationship",
+  "to" :
+  [
+    {
+      "creationInfo" : "_:Build#CreationInfo",
+      "simplelicensing_licenseExpression" : "BSD-3-Clause",
+      "spdxId" : "urn:foo::foo#LicenseExpression",
+      "type" : "simplelicensing_LicenseExpression"
+    }
+  ],
+  "type" : "Relationship"
+}
+]=])
+
 
 expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
 string(JSON CREATION_INFO GET "${content}" "@graph" "0")
@@ -76,5 +95,9 @@
 expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED)
 expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement")
 expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element")
-string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2")
+string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "3")
 expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED)
+
+# Check that default_license from the imported CPS package is reported
+string(JSON LICENSE_RELATIONSHIP GET "${content}" "@graph" "2")
+expect_object("${LICENSE_RELATIONSHIP}" LICENSE_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/cmake/test-config.cmake b/Tests/RunCMake/Sbom/cmake/test-config.cmake
index 3c46ea6..12cea73 100644
--- a/Tests/RunCMake/Sbom/cmake/test-config.cmake
+++ b/Tests/RunCMake/Sbom/cmake/test-config.cmake
@@ -1 +1,2 @@
 add_library(test::liba INTERFACE IMPORTED)
+set_target_properties(test::liba PROPERTIES SPDX_LICENSE license-for-test-liba)
diff --git a/Tests/RunCMake/XcodeProject/DeterministicProductIds-check.cmake b/Tests/RunCMake/XcodeProject/DeterministicProductIds-check.cmake
new file mode 100644
index 0000000..3c86e6f
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/DeterministicProductIds-check.cmake
@@ -0,0 +1,49 @@
+# Collect "path -> object id" for every product PBXFileReference (those under
+# BUILT_PRODUCTS_DIR) in a generated project.pbxproj.
+function(read_product_ids pbxproj out_var)
+  if(NOT EXISTS "${pbxproj}")
+    set(RunCMake_TEST_FAILED "Project file does not exist:\n  ${pbxproj}" PARENT_SCOPE)
+    return()
+  endif()
+  set(ids "")
+  file(STRINGS "${pbxproj}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "isa = PBXFileReference;" AND line MATCHES "sourceTree = BUILT_PRODUCTS_DIR;")
+      string(REGEX MATCH "^[ \t]*([0-9A-F]+) " _ "${line}")
+      set(id "${CMAKE_MATCH_1}")
+      string(REGEX MATCH "path = ([^;]+);" _ "${line}")
+      set(path "${CMAKE_MATCH_1}")
+      list(APPEND ids "${path}=${id}")
+    endif()
+  endforeach()
+  list(SORT ids)
+  set(${out_var} "${ids}" PARENT_SCOPE)
+endfunction()
+
+set(pbxproj "${RunCMake_TEST_BINARY_DIR}/DeterministicProductIds.xcodeproj/project.pbxproj")
+read_product_ids("${pbxproj}" this_ids)
+if(RunCMake_TEST_FAILED)
+  return()
+endif()
+
+if(NOT this_ids)
+  set(RunCMake_TEST_FAILED "No product PBXFileReference entries found in\n  ${pbxproj}")
+  return()
+endif()
+
+# On the second generation, compare against the first build tree. The ids must
+# be byte-identical even though the build tree path differs.
+if(DEFINED DeterministicProductIds_FirstBinaryDir)
+  read_product_ids(
+    "${DeterministicProductIds_FirstBinaryDir}/DeterministicProductIds.xcodeproj/project.pbxproj"
+    first_ids)
+  if(RunCMake_TEST_FAILED)
+    return()
+  endif()
+  if(NOT this_ids STREQUAL first_ids)
+    string(REPLACE ";" "\n  " a "${first_ids}")
+    string(REPLACE ";" "\n  " b "${this_ids}")
+    set(RunCMake_TEST_FAILED
+      "Product PBXFileReference ids are not deterministic across build trees.\nFirst:\n  ${a}\nSecond:\n  ${b}")
+  endif()
+endif()
diff --git a/Tests/RunCMake/XcodeProject/DeterministicProductIds.cmake b/Tests/RunCMake/XcodeProject/DeterministicProductIds.cmake
new file mode 100644
index 0000000..53350f2
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/DeterministicProductIds.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+# Each target's product PBXFileReference must get a deterministic object id so
+# that a parent project embedding this one can keep a stable remoteGlobalIDString.
+add_library(productStatic STATIC empty.c)
+add_library(productShared SHARED empty.c)
+add_executable(productExe empty.c)
+
+# A target in a subdirectory exercises the relative-path component of the key.
+add_subdirectory(DeterministicProductIdsSub)
diff --git a/Tests/RunCMake/XcodeProject/DeterministicProductIdsSub/CMakeLists.txt b/Tests/RunCMake/XcodeProject/DeterministicProductIdsSub/CMakeLists.txt
new file mode 100644
index 0000000..bfbf6a8
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/DeterministicProductIdsSub/CMakeLists.txt
@@ -0,0 +1 @@
+add_library(productSubStatic STATIC ../empty.c)
diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
index eb8993e..d5162b5 100644
--- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
@@ -14,6 +14,19 @@
 run_cmake(ExplicitCMakeLists)
 run_cmake(ImplicitCMakeLists)
 run_cmake(InterfaceLibSources)
+
+function(DeterministicProductIds)
+  # Generate the same project into two separate build trees and verify that the
+  # product PBXFileReference object ids are byte-identical: deterministic and
+  # independent of the build tree location.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DeterministicProductIds-build1)
+  run_cmake(DeterministicProductIds)
+  set(DeterministicProductIds_FirstBinaryDir ${RunCMake_TEST_BINARY_DIR})
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DeterministicProductIds-build2)
+  run_cmake(DeterministicProductIds)
+endfunction()
+DeterministicProductIds()
+
 run_cmake_with_options(SearchPaths -DCMAKE_CONFIGURATION_TYPES=Debug)
 run_cmake_with_options(InheritedParameters -DCMake_TEST_Swift=${CMake_TEST_Swift})
 
diff --git a/Tests/RunCMake/XcodeProject/empty.c b/Tests/RunCMake/XcodeProject/empty.c
new file mode 100644
index 0000000..af917d0
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/empty.c
@@ -0,0 +1,4 @@
+int productSymbol(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN2-stderr.txt b/Tests/RunCMake/cmake_parse_arguments/BadArgN2-stderr.txt
index 806756a..dcb6341 100644
--- a/Tests/RunCMake/cmake_parse_arguments/BadArgN2-stderr.txt
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN2-stderr.txt
@@ -1,5 +1,5 @@
 ^CMake Error at BadArgN2\.cmake:[0-9]+ \(cmake_parse_arguments\):
-  PARSE_ARGN called with _FUNCTION_ARGNC='' that is not an unsigned integer
+  PARSE_ARGN called with ARGNC='' that is not an unsigned integer
 Call Stack \(most recent call first\):
   BadArgN2\.cmake:[0-9]+ \(test2\)
   CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_parse_arguments/BadArgN2.cmake b/Tests/RunCMake/cmake_parse_arguments/BadArgN2.cmake
index 0e91679..476d0bc 100644
--- a/Tests/RunCMake/cmake_parse_arguments/BadArgN2.cmake
+++ b/Tests/RunCMake/cmake_parse_arguments/BadArgN2.cmake
@@ -1,5 +1,5 @@
 function(test2)
-  unset(_FUNCTION_ARGNC)
+  unset(ARGNC)
   cmake_parse_arguments(PARSE_ARGN pref "" "" "")
 endfunction()
 test2()
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index 9408443..b628597 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -95,7 +95,7 @@
 run_cmake_with_options(PACKAGE_INFO-AbsoluteDest-error -Werror=install-absolute-destination)
 run_cmake_with_options(SBOM-AbsoluteDest-warn
   -Winstall-absolute-destination
-  -DCMAKE_EXPERIMENTAL_GENERATE_SBOM=2d856d6d-53e8-488b-a17f-d486d2cac317)
+  -DCMAKE_EXPERIMENTAL_GENERATE_SBOM=248471c2-d905-4c9e-81b5-b89cd27965e1)
 run_cmake_with_options(IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn -Winstall-absolute-destination)
 if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$")
   run_cmake_with_options(RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn -Winstall-absolute-destination)