diff --git Modules/Internal/CPack/CPackFreeBSD.cmake Modules/Internal/CPack/CPackFreeBSD.cmake
index ae40532017..c35089cbcf 100644
--- Modules/Internal/CPack/CPackFreeBSD.cmake
+++ Modules/Internal/CPack/CPackFreeBSD.cmake
@@ -34,7 +34,7 @@ function(_cpack_freebsd_fallback_var OUTPUT_VAR_NAME)
     endif()
   endforeach()
   if(NOT VALUE)
-    message(WARNING "Variable ${OUTPUT_VAR_NAME} could not be given a fallback value from any variable ${FALLBACK_VAR_NAMES}.")
+    message(WARNING "Variable ${OUTPUT_VAR_NAME} could not be given a fallback value from (any of) ${FALLBACK_VAR_NAMES}.")
   endif()
 endfunction()
 
diff --git Source/CPack/cmCPackFreeBSDGenerator.cxx Source/CPack/cmCPackFreeBSDGenerator.cxx
index 39ec3c83ed..c09989ceed 100644
--- Source/CPack/cmCPackFreeBSDGenerator.cxx
+++ Source/CPack/cmCPackFreeBSDGenerator.cxx
@@ -21,8 +21,15 @@
 
 #include <sys/stat.h>
 
+// Suffix used to tell libpkg what compression to use
+static const char FreeBSDPackageCompression[] = "txz";
+// Resulting package file-suffix, for < 1.17 and >= 1.17 versions of libpkg
+static const char FreeBSDPackageSuffix_10[] = ".txz";
+static const char FreeBSDPackageSuffix_17[] = ".pkg";
+
 cmCPackFreeBSDGenerator::cmCPackFreeBSDGenerator()
-  : cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr", ".txz")
+  : cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr",
+                            FreeBSDPackageSuffix_17 /* old-style, updated if an old-style package is created */)
 {
 }
 
@@ -35,6 +42,56 @@ int cmCPackFreeBSDGenerator::InitializeInternal()
 
 cmCPackFreeBSDGenerator::~cmCPackFreeBSDGenerator() = default;
 
+// This is a wrapper for struct pkg_create and pkg_create()
+//
+// Instantiate this class with suitable parameters, then
+// check isValid() to check if it's ok. Afterwards, call
+// Create() to do the actual work. This will leave a package
+// in the given `output_dir`.
+//
+// This wrapper cleans up the struct pkg_create.
+class PkgCreate
+{
+public:
+  PkgCreate()
+    : d(nullptr)
+  {
+  }
+  PkgCreate(const std::string& output_dir, const std::string& toplevel_dir,
+            const std::string& manifest_name)
+    : d(pkg_create_new())
+    , manifest(manifest_name)
+
+  {
+    if (d) {
+      pkg_create_set_format(d, FreeBSDPackageCompression); // Skip over the '.'
+      pkg_create_set_compression_level(d, 0); // Explicitly set default
+      pkg_create_set_overwrite(d, false);
+      pkg_create_set_rootdir(d, toplevel_dir.c_str());
+      pkg_create_set_output_dir(d, output_dir.c_str());
+    }
+  }
+  ~PkgCreate()
+  {
+    if (d)
+      pkg_create_free(d);
+  }
+
+  bool isValid() const { return d; }
+
+  bool Create()
+  {
+    if (!isValid())
+      return false;
+    int r = pkg_create(d, manifest.c_str(), nullptr, false);
+    return r == 0;
+  }
+
+private:
+  struct pkg_create* d;
+  std::string manifest;
+};
+
 // This is a wrapper, for use only in stream-based output,
 // that will output a string in UCL escaped fashion (in particular,
 // quotes and backslashes are escaped). The list of characters
@@ -271,7 +328,7 @@ void write_manifest_files(cmGeneratedFileStream& s,
   s << "\"files\": {\n";
   for (std::string const& file : files) {
     s << "  \"/" << cmSystemTools::RelativePath(toplevel, file) << "\": \""
-      << "<sha256>"
+      << "<sha256>" // this gets replaced by libpkg by the actual SHA256
       << "\",\n";
   }
   s << "  },\n";
@@ -281,11 +338,10 @@ int cmCPackFreeBSDGenerator::PackageFiles()
 {
   if (!this->ReadListFile("Internal/CPack/CPackFreeBSD.cmake")) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Error while execution CPackFreeBSD.cmake" << std::endl);
+                  "Error while executing CPackFreeBSD.cmake" << std::endl);
     return 0;
   }
 
-  std::vector<std::string>::const_iterator fileIt;
   cmWorkingDirectory wd(toplevel);
 
   files.erase(std::remove_if(files.begin(), files.end(), ignore_file),
@@ -317,20 +373,79 @@ int cmCPackFreeBSDGenerator::PackageFiles()
                              ONE_PACKAGE_PER_COMPONENT);
   }
 
+  // There should be one name in the packageFileNames (already, see comment
+  // in cmCPackGenerator::DoPackage(), which holds what CPack guesses
+  // will be the package filename. libpkg does something else, though,
+  // so update the single filename to what we know will be right.
+  if (this->packageFileNames.size() == 1) {
+    std::string currentPackage = this->packageFileNames[0];
+    int lastSlash = currentPackage.rfind('/');
+
+    // If there is a pathname, preserve that; libpkg will write out
+    // a file with the package name and version as specified in the
+    // manifest, so we look those up (again). lastSlash is the slash
+    // itself, we need that as path separator to the calculated package name.
+    std::string actualPackage =
+      ((lastSlash != std::string::npos)
+         ? std::string(currentPackage, 0, lastSlash + 1)
+         : std::string()) +
+      var_lookup("CPACK_FREEBSD_PACKAGE_NAME") + '-' +
+      var_lookup("CPACK_FREEBSD_PACKAGE_VERSION") + FreeBSDPackageSuffix_17;
+
+    this->packageFileNames.clear();
+    this->packageFileNames.emplace_back(actualPackage);
+
+    cmCPackLogger(cmCPackLog::LOG_DEBUG,
+                  "Real filename:" << this->packageFileNames[0] << std::endl);
+  }
+
+  if (!pkg_initialized() && pkg_init(NULL, NULL) != EPKG_OK) {
+    cmCPackLogger(cmCPackLog::LOG_ERROR,
+                  "Can not initialize FreeBSD libpkg." << std::endl);
+    return 0;
+  }
+
   std::string output_dir = cmSystemTools::CollapseFullPath("../", toplevel);
-  pkg_create_from_manifest(output_dir.c_str(), ::TXZ, toplevel.c_str(),
-                           manifestname.c_str(), nullptr);
+  PkgCreate package(output_dir, toplevel, manifestname);
+  if (package.isValid()) {
+    if (!package.Create()) {
+      cmCPackLogger(cmCPackLog::LOG_ERROR,
+                    "Error during pkg_create()" << std::endl);
+      return 0;
+    }
+  } else {
+    cmCPackLogger(cmCPackLog::LOG_ERROR,
+                  "Error before pkg_create()" << std::endl);
+    return 0;
+  }
 
-  std::string broken_suffix =
-    cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), ".txz");
+  // Specifically looking for packages suffixed with the TAG, either extension
+  std::string broken_suffix_10 =
+    cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_10);
+  std::string broken_suffix_17 =
+    cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_17);
   for (std::string& name : packageFileNames) {
     cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << name << std::endl);
-    if (cmHasSuffix(name, broken_suffix)) {
-      name.replace(name.size() - broken_suffix.size(), std::string::npos,
-                   ".txz");
+    if (cmHasSuffix(name, broken_suffix_10)) {
+      name.replace(name.size() - broken_suffix_10.size(), std::string::npos,
+                   FreeBSDPackageSuffix_10);
+      break;
+    }
+    if (cmHasSuffix(name, broken_suffix_17)) {
+      name.replace(name.size() - broken_suffix_17.size(), std::string::npos,
+                   FreeBSDPackageSuffix_17);
       break;
     }
   }
+  // If the name uses a *new* style name, which doesn't exist, but there
+  // is an *old* style name, then use that instead. This indicates we used
+  // an older libpkg, which still creates .txz instead of .pkg files.
+  for (std::string& name : packageFileNames) {
+    if (cmHasSuffix(name, FreeBSDPackageSuffix_17) && !cmSystemTools::FileExists(name)) {
+      const std::string suffix(FreeBSDPackageSuffix_17);
+      name.replace(name.size() - suffix.size(), std::string::npos, suffix);
+    }
+  }
 
   return 1;
 }
