Commit f739752a authored by Alex Turbov's avatar Alex Turbov Committed by Brad King

CPack: Add NuGet support

Create a CPack generator that uses `nuget.exe` to create packages:

    https://docs.microsoft.com/en-us/nuget/what-is-nuget

NuGet packages could be easily produced from a `*.nuspec` file (running
`nuget pack` in the directory w/ the spec file).  The spec filename does
not affect the result `*.nupkg` name -- only `id` and `version` elements
of the spec are used (by NuGet).

Some implementation details:

* Minimize C++ code -- use CMake script do to the job. It just let the
  base class (`cmCPackGenerator`) to preinstall everything to a temp
  directory, render the spec file and run `nuget pack` in it, harvesting
  `*.nupkg` files...;

* Ignore package name (and use default paths) prepared by the base class
  (only `CPACK_TEMPORARY_DIRECTORY` is important) -- final package
  filename is a responsibility of NuGet, so after generation just scan the
  temp directory for the result `*.nupkg` file(s) and update
  `packageFileNames` data-member of the generator;

* The generator supports _all-in-one_ (default), _one-group-per-package_
  and _one-component-per-package_ modes.
parent dd43e6fe
......@@ -64,6 +64,7 @@ All Modules
/module/CPackIFW
/module/CPackIFWConfigureFile
/module/CPackNSIS
/module/CPackNuGet
/module/CPackPackageMaker
/module/CPackProductBuild
/module/CPackRPM
......
.. cmake-module:: ../../Modules/CPackNuGet.cmake
cpack-nuget
-----------
* :manual:`cpack(1)` gained basic support for `NuGet`_.
See the :module:`CPackNuGet` module.
.. _NuGet: https://docs.microsoft.com/en-us/nuget/what-is-nuget
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<!-- Required elements-->
<id>@CPACK_NUGET_PACKAGE_NAME@</id>
<version>@CPACK_NUGET_PACKAGE_VERSION@</version>
<description>@CPACK_NUGET_PACKAGE_DESCRIPTION@</description>
<authors>@CPACK_NUGET_PACKAGE_AUTHORS@</authors>
<!-- Optional elements -->
@_CPACK_NUGET_TITLE_TAG@
@_CPACK_NUGET_OWNERS_TAG@
@_CPACK_NUGET_PROJECTURL_TAG@
@_CPACK_NUGET_LICENSEURL_TAG@
@_CPACK_NUGET_ICONURL_TAG@
@_CPACK_NUGET_REQUIRELICENSEACCEPTANCE_TAG@
@_CPACK_NUGET_SUMMARY_TAG@
@_CPACK_NUGET_RELEASENOTES_TAG@
@_CPACK_NUGET_COPYRIGHT_TAG@
@_CPACK_NUGET_TAGS_TAG@
@_CPACK_NUGET_DEPENDENCIES_TAG@
</metadata>
@_CPACK_NUGET_FILES_TAG@
</package>
......@@ -544,10 +544,11 @@ if(NOT CPACK_GENERATOR)
option(CPACK_BINARY_TXZ "Enable to build TXZ packages" OFF)
endif()
else()
option(CPACK_BINARY_7Z "Enable to build 7-Zip packages" OFF)
option(CPACK_BINARY_NSIS "Enable to build NSIS packages" ON)
option(CPACK_BINARY_WIX "Enable to build WiX packages" OFF)
option(CPACK_BINARY_ZIP "Enable to build ZIP packages" OFF)
option(CPACK_BINARY_7Z "Enable to build 7-Zip packages" OFF)
option(CPACK_BINARY_NSIS "Enable to build NSIS packages" ON)
option(CPACK_BINARY_NUGET "Enable to build NuGet packages" OFF)
option(CPACK_BINARY_WIX "Enable to build WiX packages" OFF)
option(CPACK_BINARY_ZIP "Enable to build ZIP packages" OFF)
endif()
option(CPACK_BINARY_IFW "Enable to build IFW packages" OFF)
......@@ -559,6 +560,7 @@ if(NOT CPACK_GENERATOR)
cpack_optional_append(CPACK_GENERATOR CPACK_BINARY_FREEBSD FREEBSD)
cpack_optional_append(CPACK_GENERATOR CPACK_BINARY_IFW IFW)
cpack_optional_append(CPACK_GENERATOR CPACK_BINARY_NSIS NSIS)
cpack_optional_append(CPACK_GENERATOR CPACK_BINARY_NUGET NuGet)
cpack_optional_append(CPACK_GENERATOR CPACK_BINARY_OSXX11 OSXX11)
cpack_optional_append(CPACK_GENERATOR CPACK_BINARY_PACKAGEMAKER PackageMaker)
cpack_optional_append(CPACK_GENERATOR CPACK_BINARY_PRODUCTBUILD productbuild)
......@@ -611,6 +613,7 @@ mark_as_advanced(
CPACK_BINARY_FREEBSD
CPACK_BINARY_IFW
CPACK_BINARY_NSIS
CPACK_BINARY_NUGET
CPACK_BINARY_OSXX11
CPACK_BINARY_PACKAGEMAKER
CPACK_BINARY_PRODUCTBUILD
......
This diff is collapsed.
......@@ -886,6 +886,7 @@ set(CPACK_SRCS
CPack/cmCPackGenerator.cxx
CPack/cmCPackLog.cxx
CPack/cmCPackNSISGenerator.cxx
CPack/cmCPackNuGetGenerator.cxx
CPack/cmCPackSTGZGenerator.cxx
CPack/cmCPackTGZGenerator.cxx
CPack/cmCPackTXZGenerator.cxx
......
......@@ -15,6 +15,7 @@
#include "cmCPackGenerator.h"
#include "cmCPackLog.h"
#include "cmCPackNSISGenerator.h"
#include "cmCPackNuGetGenerator.h"
#include "cmCPackSTGZGenerator.h"
#include "cmCPackTGZGenerator.h"
#include "cmCPackTXZGenerator.h"
......@@ -105,6 +106,10 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory()
this->RegisterGenerator("DEB", "Debian packages",
cmCPackDebGenerator::CreateGenerator);
}
if (cmCPackNuGetGenerator::CanGenerate()) {
this->RegisterGenerator("NuGet", "NuGet packages",
cmCPackNuGetGenerator::CreateGenerator);
}
#ifdef __APPLE__
if (cmCPackDragNDropGenerator::CanGenerate()) {
this->RegisterGenerator("DragNDrop", "Mac OSX Drag And Drop",
......
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCPackNuGetGenerator.h"
#include "cmAlgorithms.h"
#include "cmCPackComponentGroup.h"
#include "cmCPackLog.h"
#include "cmSystemTools.h"
#include <algorithm>
#include <iterator>
#include <map>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
bool cmCPackNuGetGenerator::SupportsComponentInstallation() const
{
return IsOn("CPACK_NUGET_COMPONENT_INSTALL");
}
int cmCPackNuGetGenerator::PackageFiles()
{
cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl);
/* Reset package file name list it will be populated after the
* `CPackNuGet.cmake` run */
packageFileNames.clear();
/* Are we in the component packaging case */
if (WantsComponentInstallation()) {
if (componentPackageMethod == ONE_PACKAGE) {
// CASE 1 : COMPONENT ALL-IN-ONE package
// Meaning that all per-component pre-installed files
// goes into the single package.
this->SetOption("CPACK_NUGET_ALL_IN_ONE", "TRUE");
SetupGroupComponentVariables(true);
} else {
// CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
// There will be 1 package for each component group
// however one may require to ignore component group and
// in this case you'll get 1 package for each component.
SetupGroupComponentVariables(componentPackageMethod ==
ONE_PACKAGE_PER_COMPONENT);
}
} else {
// CASE 3 : NON COMPONENT package.
this->SetOption("CPACK_NUGET_ORDINAL_MONOLITIC", "TRUE");
}
auto retval = this->ReadListFile("CPackNuGet.cmake");
if (retval) {
AddGeneratedPackageNames();
} else {
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Error while execution CPackNuGet.cmake" << std::endl);
}
return retval;
}
void cmCPackNuGetGenerator::SetupGroupComponentVariables(bool ignoreGroup)
{
// The default behavior is to have one package by component group
// unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
if (!ignoreGroup) {
std::vector<std::string> groups;
for (auto const& compG : this->ComponentGroups) {
cmCPackLogger(cmCPackLog::LOG_VERBOSE,
"Packaging component group: " << compG.first << std::endl);
groups.push_back(compG.first);
auto compGUp =
cmSystemTools::UpperCase(cmSystemTools::MakeCidentifier(compG.first));
// Collect components for this group
std::vector<std::string> components;
std::transform(begin(compG.second.Components),
end(compG.second.Components),
std::back_inserter(components),
[](cmCPackComponent const* comp) { return comp->Name; });
this->SetOption("CPACK_NUGET_" + compGUp + "_GROUP_COMPONENTS",
cmJoin(components, ";").c_str());
}
if (!groups.empty()) {
this->SetOption("CPACK_NUGET_GROUPS", cmJoin(groups, ";").c_str());
}
// Handle Orphan components (components not belonging to any groups)
std::vector<std::string> components;
for (auto const& comp : this->Components) {
// Does the component belong to a group?
if (comp.second.Group == nullptr) {
cmCPackLogger(
cmCPackLog::LOG_VERBOSE, "Component <"
<< comp.second.Name
<< "> does not belong to any group, package it separately."
<< std::endl);
components.push_back(comp.first);
}
}
if (!components.empty()) {
this->SetOption("CPACK_NUGET_COMPONENTS",
cmJoin(components, ";").c_str());
}
} else {
std::vector<std::string> components;
components.reserve(this->Components.size());
std::transform(begin(this->Components), end(this->Components),
std::back_inserter(components),
[](std::pair<std::string, cmCPackComponent> const& comp) {
return comp.first;
});
this->SetOption("CPACK_NUGET_COMPONENTS", cmJoin(components, ";").c_str());
}
}
void cmCPackNuGetGenerator::AddGeneratedPackageNames()
{
const char* const files_list = this->GetOption("GEN_CPACK_OUTPUT_FILES");
if (!files_list) {
cmCPackLogger(
cmCPackLog::LOG_ERROR,
"Error while execution CPackNuGet.cmake: No NuGet package has generated"
<< std::endl);
return;
}
// add the generated packages to package file names list
std::string fileNames{ files_list };
const char sep = ';';
std::string::size_type pos1 = 0;
std::string::size_type pos2 = fileNames.find(sep, pos1 + 1);
while (pos2 != std::string::npos) {
packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1));
pos1 = pos2 + 1;
pos2 = fileNames.find(sep, pos1 + 1);
}
packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1));
}
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#ifndef cmCPackNuGetGenerator_h
#define cmCPackNuGetGenerator_h
#include "cmCPackGenerator.h"
/** \class cmCPackNuGetGenerator
* \brief A generator for RPM packages
*/
class cmCPackNuGetGenerator : public cmCPackGenerator
{
public:
cmCPackTypeMacro(cmCPackNuGetGenerator, cmCPackGenerator);
// NOTE In fact, it is possible to have NuGet not only for Windows...
// https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools
static bool CanGenerate() { return true; }
protected:
bool SupportsComponentInstallation() const override;
int PackageFiles() override;
const char* GetOutputExtension() override { return ".nupkg"; }
bool SupportsAbsoluteDestination() const override { return false; }
/**
* The method used to prepare variables when component
* install is used.
*/
void SetupGroupComponentVariables(bool ignoreGroup);
/**
* Populate \c packageFileNames vector of built packages.
*/
void AddGeneratedPackageNames();
};
#endif
......@@ -154,6 +154,15 @@ if(BUILD_TESTING)
set(CPACK_BINARY_DEB OFF)
endif()
# Look for NuGet to use for tests.
find_program(NUGET_EXECUTABLE NAMES NuGet nuget)
if(NUGET_EXECUTABLE)
set(CPACK_BINARY_NUGET ON)
else()
set(CPACK_BINARY_NUGET OFF)
endif()
#---------------------------------------------------------------------------
# Add tests below here.
......@@ -1032,6 +1041,12 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
if(CPACK_BINARY_DEB)
list(APPEND ACTIVE_CPACK_GENERATORS DEB)
endif()
# Check whether if NuGet command is found
# before adding NuGet tests
if(CPACK_BINARY_NUGET)
list(APPEND ACTIVE_CPACK_GENERATORS NUGET)
set(CPACK_GENERATOR_STRING_NUGET NuGet)
endif()
# ACTIVE_CPACK_GENERATORS variable
# now contains the list of 'active generators'
......@@ -1051,7 +1066,10 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
list(APPEND CWAYLST "IgnoreGroup")
list(APPEND CWAYLST "AllInOne")
foreach(CPackGen IN LISTS ACTIVE_CPACK_GENERATORS)
set(CPackRun_CPackGen "-DCPackGen=${CPackGen}")
if(NOT DEFINED CPACK_GENERATOR_STRING_${CPackGen})
set(CPACK_GENERATOR_STRING_${CPackGen} ${CPackGen})
endif()
set(CPackRun_CPackGen "-DCPackGen=${CPACK_GENERATOR_STRING_${CPackGen}}")
foreach(CPackComponentWay ${CWAYLST})
set(CPackRun_CPackComponentWay "-DCPackComponentWay=${CPackComponentWay}")
add_test(CPackComponentsForAll-${CPackGen}-${CPackComponentWay}
......@@ -1062,7 +1080,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
${build_generator_args}
--build-project CPackComponentsForAll
--build-options ${build_options}
-DCPACK_GENERATOR:STRING=${CPackGen}
-DCPACK_GENERATOR:STRING=${CPACK_GENERATOR_STRING_${CPackGen}}
-DCPACK_BINARY_${CPackGen}:BOOL=ON
${CPackRun_CPackComponentWay}
${CPackComponentsForAll_BUILD_OPTIONS}
......
......@@ -168,6 +168,18 @@ set(CPACK_RPM_RELOCATION_PATHS "${CMAKE_INSTALL_INCLUDEDIR}"
# set CPACK_DEBIAN_FILE_NAME to use default package name format
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
# set some tags for NuGet packages
# 1. all in one pacakge
set(CPACK_NUGET_PACKAGE_TAGS "nuget" "unit" "test" "all-in-one")
# 2. per component packages
set(CPACK_NUGET_APPLICATIONS_PACKAGE_TAGS "nuget" "unit" "test" "applications")
set(CPACK_NUGET_LIBRARIES_PACKAGE_TAGS "nuget" "unit" "test" "libraries")
set(CPACK_NUGET_HEADERS_PACKAGE_TAGS "nuget" "unit" "test" "headers")
set(CPACK_NUGET_UNSPECIFIED_PACKAGE_TAGS "nuget" "unit" "test" "uNsP3c1FiEd")
# 3. per group packages
set(CPACK_NUGET_RUNTIME_PACKAGE_TAGS "nuget" "unit" "test" "run-time")
set(CPACK_NUGET_DEVELOPMENT_PACKAGE_TAGS "nuget" "unit" "test" "development")
# We may use the CPack specific config file in order
# to tailor CPack behavior on a CPack generator specific way
# (Behavior would be different for RPM or TGZ or DEB ...)
......
......@@ -13,6 +13,10 @@ if(CPACK_GENERATOR MATCHES "DEB")
set(CPACK_DEB_COMPONENT_INSTALL "ON")
endif()
if(CPACK_GENERATOR MATCHES "NuGet")
set(CPACK_NUGET_COMPONENT_INSTALL "ON")
endif()
#
# Choose grouping way
#
......
......@@ -52,6 +52,10 @@ if(CPACK_GENERATOR MATCHES "DEB")
set(CPACK_DEB_COMPONENT_INSTALL "ON")
endif()
if(CPACK_GENERATOR MATCHES "NuGet")
set(CPACK_NUGET_COMPONENT_INSTALL "ON")
endif()
#
# Choose grouping way
#
......
......@@ -18,6 +18,10 @@ if(CPACK_GENERATOR MATCHES "DragNDrop")
set(CPACK_COMPONENTS_GROUPING "ONE_PER_GROUP")
endif()
if(CPACK_GENERATOR MATCHES "NuGet")
set(CPACK_NUGET_COMPONENT_INSTALL "ON")
endif()
#
# Choose grouping way
#
......
......@@ -70,6 +70,18 @@ elseif(CPackGen MATCHES "DEB")
elseif(${CPackComponentWay} STREQUAL "AllInOne")
set(expected_count 1)
endif()
elseif(CPackGen MATCHES "NuGet")
set(config_verbose -D "CPACK_NUGET_PACKAGE_DEBUG=1")
set(expected_file_mask "${CPackComponentsForAll_BINARY_DIR}/MyLib*1.0.2.nupkg")
if(${CPackComponentWay} STREQUAL "default")
set(expected_count 1)
elseif(${CPackComponentWay} STREQUAL "OnePackPerGroup")
set(expected_count 3)
elseif(${CPackComponentWay} STREQUAL "IgnoreGroup")
set(expected_count 4)
elseif(${CPackComponentWay} STREQUAL "AllInOne")
set(expected_count 1)
endif()
endif()
if(CPackGen MATCHES "DragNDrop")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment