Commit 9a0ba4d2 authored by Konstantin Podsvirov's avatar Konstantin Podsvirov Committed by Brad King

CPackIFW: Add QtIFW 2.0 support

Add variables:

- CPACK_IFW_FRAMEWORK_VERSION
- CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS
- CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH
- CPACK_IFW_PACKAGE_CONTROL_SCRIPT
- CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE
- CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_NAME
- CPACK_IFW_PACKAGE_START_MENU_DIRECTORY
- CPACK_IFW_VERBOSE
parent f6a41a44
cpack-ifw-updates
-----------------
* The :manual:`cpack(1)` ``IFW`` generator and the :module:`CPackIFW`
module learned to support Qt Framework Installer 2.0 tools.
......@@ -2,7 +2,7 @@
# CPackIFW
# --------
#
# .. _QtIFW: http://qt-project.org/doc/qtinstallerframework/index.html
# .. _QtIFW: http://doc.qt.io/qtinstallerframework/index.html
#
# This module looks for the location of the command line utilities supplied with
# the Qt Installer Framework (QtIFW_).
......@@ -33,18 +33,26 @@
#
# You can use the following variables to change behavior of CPack ``IFW`` generator.
#
# Debug
# """"""
#
# .. variable:: CPACK_IFW_VERBOSE
#
# Set to ``ON`` to enable addition debug output.
# By default is ``OFF``.
#
# Package
# """""""
#
# .. variable:: CPACK_IFW_PACKAGE_TITLE
#
# Name of the installer as displayed on the title bar.
# By default used :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY`
# By default used :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY`.
#
# .. variable:: CPACK_IFW_PACKAGE_PUBLISHER
#
# Publisher of the software (as shown in the Windows Control Panel).
# By default used :variable:`CPACK_PACKAGE_VENDOR`
# By default used :variable:`CPACK_PACKAGE_VENDOR`.
#
# .. variable:: CPACK_IFW_PRODUCT_URL
#
......@@ -63,6 +71,12 @@
#
# Filename for a logo is used as QWizard::LogoPixmap.
#
# .. variable:: CPACK_IFW_PACKAGE_START_MENU_DIRECTORY
#
# Name of the default program group for the product in the Windows Start menu.
#
# By default used :variable:`CPACK_IFW_PACKAGE_NAME`.
#
# .. variable:: CPACK_IFW_TARGET_DIRECTORY
#
# Default target directory for installation.
......@@ -85,6 +99,35 @@
# The root package name, which will be used if configuration group is not
# specified
#
# .. variable:: CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_NAME
#
# Filename of the generated maintenance tool.
# The platform-specific executable file extension is appended.
#
# By default used QtIFW_ defaults (``maintenancetool``).
#
# .. variable:: CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE
#
# Filename for the configuration of the generated maintenance tool.
#
# By default used QtIFW_ defaults (``maintenancetool.ini``).
#
# .. variable:: CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS
#
# Set to ``ON`` if the installation path can contain non-ASCII characters.
#
# Is ``ON`` for QtIFW_ less 2.0 tools.
#
# .. variable:: CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH
#
# Set to ``OFF`` if the installation path cannot contain space characters.
#
# Is ``ON`` for QtIFW_ less 2.0 tools.
#
# .. variable:: CPACK_IFW_PACKAGE_CONTROL_SCRIPT
#
# Filename for a custom installer control script.
#
# .. variable:: CPACK_IFW_REPOSITORIES_ALL
#
# The list of remote repositories.
......@@ -113,6 +156,10 @@
# Tools
# """"""""
#
# .. variable:: CPACK_IFW_FRAMEWORK_VERSION
#
# The version of used QtIFW_ tools.
#
# .. variable:: CPACK_IFW_BINARYCREATOR_EXECUTABLE
#
# The path to "binarycreator" command line client.
......@@ -276,16 +323,16 @@
# Qt Installer Framework Manual:
#
# Index page
# http://qt-project.org/doc/qtinstallerframework/index.html
# http://doc.qt.io/qtinstallerframework/index.html
#
# Component Scripting
# http://qt-project.org/doc/qtinstallerframework/scripting.html
# http://doc.qt.io/qtinstallerframework/scripting.html
#
# Predefined Variables
# http://qt-project.org/doc/qtinstallerframework/scripting.html#predefined-variables
# http://doc.qt.io/qtinstallerframework/scripting.html#predefined-variables
#
# Download Qt Installer Framework for you platform from Qt Project site:
# http://download.qt-project.org/official_releases/qt-installer-framework/
# Download Qt Installer Framework for you platform from Qt site:
# http://download.qt.io/official_releases/qt-installer-framework
#
#=============================================================================
......@@ -324,8 +371,14 @@ else()
endif()
set(_CPACK_IFW_SUFFIXES
# Common
"bin"
"QtIFW-1.7.0/bin"
# Second branch
"QtIFW2.3.0/bin"
"QtIFW2.2.0/bin"
"QtIFW2.1.0/bin"
"QtIFW2.0.0/bin"
# First branch
"QtIFW-1.6.0/bin"
"QtIFW-1.5.0/bin"
"QtIFW-1.4.0/bin"
......@@ -351,6 +404,26 @@ find_program(CPACK_IFW_REPOGEN_EXECUTABLE
)
mark_as_advanced(CPACK_IFW_REPOGEN_EXECUTABLE)
# Look for 'installerbase'
find_program(CPACK_IFW_INSTALLERBASE_EXECUTABLE
NAMES installerbase
PATHS ${_CPACK_IFW_PATHS}
PATH_SUFFIXES ${_CPACK_IFW_SUFFIXES}
DOC "QtIFW installer executable base"
)
mark_as_advanced(CPACK_IFW_INSTALLERBASE_EXECUTABLE)
# Look for 'devtool' (appeared in the second branch)
find_program(CPACK_IFW_DEVTOOL_EXECUTABLE
NAMES devtool
PATHS ${_CPACK_IFW_PATHS}
PATH_SUFFIXES ${_CPACK_IFW_SUFFIXES}
DOC "QtIFW devtool command line client"
)
mark_as_advanced(CPACK_IFW_DEVTOOL_EXECUTABLE)
#
## Next code is included only once
#
......@@ -358,6 +431,27 @@ mark_as_advanced(CPACK_IFW_REPOGEN_EXECUTABLE)
if(NOT CPackIFW_CMake_INCLUDED)
set(CPackIFW_CMake_INCLUDED 1)
#=============================================================================
# Framework version
#=============================================================================
if(CPACK_IFW_INSTALLERBASE_EXECUTABLE AND CPACK_IFW_DEVTOOL_EXECUTABLE)
execute_process(COMMAND
"${CPACK_IFW_INSTALLERBASE_EXECUTABLE}" --framework-version
OUTPUT_VARIABLE CPACK_IFW_FRAMEWORK_VERSION)
if(CPACK_IFW_FRAMEWORK_VERSION)
string(REPLACE " " ""
CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_FRAMEWORK_VERSION}")
string(REPLACE "\t" ""
CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_FRAMEWORK_VERSION}")
string(REPLACE "\n" ""
CPACK_IFW_FRAMEWORK_VERSION "${CPACK_IFW_FRAMEWORK_VERSION}")
if(CPACK_IFW_VERBOSE)
message(STATUS "Found QtIFW ${CPACK_IFW_FRAMEWORK_VERSION} version")
endif()
endif()
endif()
#=============================================================================
# Macro definition
#=============================================================================
......@@ -514,4 +608,7 @@ macro(cpack_ifw_add_repository reponame)
endmacro()
# Resolve package control script
_cpack_ifw_resolve_script(CPACK_IFW_PACKAGE_CONTROL_SCRIPT)
endif() # NOT CPackIFW_CMake_INCLUDED
......@@ -29,6 +29,8 @@
#include <cmMakefile.h>
#include <cmGeneratedFileStream.h>
#include <cmXMLSafe.h>
#include <cmVersionConfig.h>
#include <cmTimestamp.h>
//----------------------------------------------------------------------------
cmCPackIFWGenerator::cmCPackIFWGenerator()
......@@ -40,6 +42,27 @@ cmCPackIFWGenerator::~cmCPackIFWGenerator()
{
}
//----------------------------------------------------------------------------
bool cmCPackIFWGenerator::IsVersionLess(const char *version)
{
return cmSystemTools::VersionCompare(cmSystemTools::OP_LESS,
FrameworkVersion.data(), version);
}
//----------------------------------------------------------------------------
bool cmCPackIFWGenerator::IsVersionGreater(const char *version)
{
return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER,
FrameworkVersion.data(), version);
}
//----------------------------------------------------------------------------
bool cmCPackIFWGenerator::IsVersionEqual(const char *version)
{
return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL,
FrameworkVersion.data(), version);
}
//----------------------------------------------------------------------------
int cmCPackIFWGenerator::PackageFiles()
{
......@@ -59,7 +82,12 @@ int cmCPackIFWGenerator::PackageFiles()
if (!Installer.Repositories.empty())
{
std::string ifwCmd = RepoGen;
ifwCmd += " -c " + this->toplevel + "/config/config.xml";
if(IsVersionLess("2.0.0"))
{
ifwCmd += " -c " + this->toplevel + "/config/config.xml";
}
ifwCmd += " -p " + this->toplevel + "/packages";
if(!PkgsDirsVector.empty())
......@@ -216,8 +244,7 @@ const char *cmCPackIFWGenerator::GetPackagingInstallPrefix()
//----------------------------------------------------------------------------
const char *cmCPackIFWGenerator::GetOutputExtension()
{
const char *suffix = this->GetOption("CMAKE_EXECUTABLE_SUFFIX");
return suffix ? suffix : cmCPackGenerator::GetOutputExtension();
return ExecutableSuffix.c_str();
}
//----------------------------------------------------------------------------
......@@ -267,6 +294,17 @@ int cmCPackIFWGenerator::InitializeInternal()
RepoGen = RepoGenStr;
}
// Framework version
if(const char* FrameworkVersionSrt =
this->GetOption("CPACK_IFW_FRAMEWORK_VERSION"))
{
FrameworkVersion = FrameworkVersionSrt;
}
else
{
FrameworkVersion = "1.9.9";
}
// Variables that Change Behavior
// Resolve duplicate names
......@@ -307,6 +345,24 @@ int cmCPackIFWGenerator::InitializeInternal()
return 0;
}
// Executable suffix
if(const char *optExeSuffix = this->GetOption("CMAKE_EXECUTABLE_SUFFIX"))
{
ExecutableSuffix = optExeSuffix;
if(ExecutableSuffix.empty())
{
std::string sysName(this->GetOption("CMAKE_SYSTEM_NAME"));
if(sysName == "Linux")
{
ExecutableSuffix = ".run";
}
}
}
else
{
ExecutableSuffix = cmCPackGenerator::GetOutputExtension();
}
return this->Superclass::InitializeInternal();
}
......@@ -552,3 +608,20 @@ cmCPackIFWPackage* cmCPackIFWGenerator::GetComponentPackage(
= ComponentPackages.find(component);
return pit != ComponentPackages.end() ? pit->second : 0;
}
//----------------------------------------------------------------------------
void cmCPackIFWGenerator::WriteGeneratedByToStrim(cmGeneratedFileStream &xout)
{
xout << "<!-- Generated by CPack " << CMake_VERSION << " IFW generator "
<< "for QtIFW ";
if(IsVersionLess("2.0"))
{
xout << "less 2.0";
}
else
{
xout << FrameworkVersion;
}
xout << " tools at " << cmTimestamp().CurrentTime("", true) << " -->"
<< std::endl;
}
......@@ -13,6 +13,7 @@
#ifndef cmCPackIFWGenerator_h
#define cmCPackIFWGenerator_h
#include <cmGeneratedFileStream.h>
#include <CPack/cmCPackGenerator.h>
#include "cmCPackIFWPackage.h"
......@@ -44,6 +45,21 @@ public:
*/
virtual ~cmCPackIFWGenerator();
/**
* Compare \a version with QtIFW framework version
*/
bool IsVersionLess(const char *version);
/**
* Compare \a version with QtIFW framework version
*/
bool IsVersionGreater(const char *version);
/**
* Compare \a version with QtIFW framework version
*/
bool IsVersionEqual(const char *version);
protected: // cmCPackGenerator reimplementation
/**
......@@ -105,6 +121,8 @@ protected: // Methods
cmCPackIFWPackage* GetGroupPackage(cmCPackComponentGroup *group) const;
cmCPackIFWPackage* GetComponentPackage(cmCPackComponent *component) const;
void WriteGeneratedByToStrim(cmGeneratedFileStream& xout);
protected: // Data
friend class cmCPackIFWPackage;
......@@ -126,6 +144,8 @@ protected: // Data
private:
std::string RepoGen;
std::string BinCreator;
std::string FrameworkVersion;
std::string ExecutableSuffix;
bool OnlineOnly;
bool ResolveDuplicateNames;
......
......@@ -50,6 +50,25 @@ bool cmCPackIFWInstaller::IsOn(const std::string &op) const
return Generator ? Generator->IsOn(op) : false;
}
//----------------------------------------------------------------------------
bool cmCPackIFWInstaller::IsVersionLess(const char *version)
{
return Generator ? Generator->IsVersionLess(version) : false;
}
//----------------------------------------------------------------------------
bool cmCPackIFWInstaller::IsVersionGreater(const char *version)
{
return Generator ? Generator->IsVersionGreater(version) : false;
}
//----------------------------------------------------------------------------
bool cmCPackIFWInstaller::IsVersionEqual(const char *version)
{
return Generator ? Generator->IsVersionEqual(version) : false;
}
//----------------------------------------------------------------------------
void cmCPackIFWInstaller::ConfigureFromOptions()
{
......@@ -151,6 +170,17 @@ void cmCPackIFWInstaller::ConfigureFromOptions()
}
}
// Start menu
if (const char* optIFW_START_MENU_DIR =
this->GetOption("CPACK_IFW_PACKAGE_START_MENU_DIRECTORY"))
{
StartMenuDir = optIFW_START_MENU_DIR;
}
else
{
StartMenuDir = Name;
}
// Default target directory for installation
if (const char* optIFW_TARGET_DIRECTORY =
GetOption("CPACK_IFW_TARGET_DIRECTORY"))
......@@ -177,7 +207,7 @@ void cmCPackIFWInstaller::ConfigureFromOptions()
// Repositories
Repositories.clear();
RepositoryStruct Repo;
if (const char *site = this->GetOption("CPACK_DOWNLOAD_SITE"))
if(const char *site = this->GetOption("CPACK_DOWNLOAD_SITE"))
{
Repo.Url = site;
Repositories.push_back(Repo);
......@@ -245,6 +275,53 @@ void cmCPackIFWInstaller::ConfigureFromOptions()
}
}
}
// Maintenance tool
if(const char* optIFW_MAINTENANCE_TOOL =
this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_NAME"))
{
MaintenanceToolName = optIFW_MAINTENANCE_TOOL;
}
// Maintenance tool ini file
if(const char* optIFW_MAINTENANCE_TOOL_INI =
this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE"))
{
MaintenanceToolIniFile = optIFW_MAINTENANCE_TOOL_INI;
}
// Allow non-ASCII characters
if(this->GetOption("CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS"))
{
if(IsOn("CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS"))
{
AllowNonAsciiCharacters = "true";
}
else
{
AllowNonAsciiCharacters = "false";
}
}
// Space in path
if(this->GetOption("CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH"))
{
if(IsOn("CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH"))
{
AllowSpaceInPath = "true";
}
else
{
AllowSpaceInPath = "false";
}
}
// Control script
if(const char* optIFW_CONTROL_SCRIPT =
this->GetOption("CPACK_IFW_PACKAGE_CONTROL_SCRIPT"))
{
ControlScript = optIFW_CONTROL_SCRIPT;
}
}
//----------------------------------------------------------------------------
......@@ -259,7 +336,10 @@ void cmCPackIFWInstaller::GenerateInstallerFile()
// Output stream
cmGeneratedFileStream xout((Directory + "/config/config.xml").data());
xout << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
xout << "<?xml version=\"1.0\"?>" << std::endl;
WriteGeneratedByToStrim(xout);
xout << "<Installer>" << std::endl;
xout << " <Name>" << cmXMLSafe(Name).str() << "</Name>" << std::endl;
......@@ -313,11 +393,20 @@ void cmCPackIFWInstaller::GenerateInstallerFile()
xout << " <Logo>" << name << "</Logo>" << std::endl;
}
// Start menu
if(!IsVersionLess("2.0"))
{
xout << " <StartMenuDir>" << StartMenuDir
<< "</StartMenuDir>" << std::endl;
}
// Target dir
if(!TargetDir.empty())
{
xout << " <TargetDir>" << TargetDir << "</TargetDir>" << std::endl;
}
// Admin target dir
if(!AdminTargetDir.empty())
{
xout << " <AdminTargetDir>" << AdminTargetDir
......@@ -364,11 +453,52 @@ void cmCPackIFWInstaller::GenerateInstallerFile()
xout << " </RemoteRepositories>" << std::endl;
}
// CPack IFW default policy
xout << " <!-- CPack IFW default policy -->" << std::endl;
xout << " <AllowNonAsciiCharacters>true</AllowNonAsciiCharacters>"
<< std::endl;
xout << " <AllowSpaceInPath>true</AllowSpaceInPath>" << std::endl;
// Maintenance tool
if(!IsVersionLess("2.0") && !MaintenanceToolName.empty())
{
xout << " <MaintenanceToolName>" << MaintenanceToolName
<< "</MaintenanceToolName>" << std::endl;
}
// Maintenance tool ini file
if(!IsVersionLess("2.0") && !MaintenanceToolIniFile.empty())
{
xout << " <MaintenanceToolIniFile>" << MaintenanceToolIniFile
<< "</MaintenanceToolIniFile>" << std::endl;
}
// Different allows
if(IsVersionLess("2.0"))
{
// CPack IFW default policy
xout << " <!-- CPack IFW default policy for QtIFW less 2.0 -->"
<< std::endl;
xout << " <AllowNonAsciiCharacters>true</AllowNonAsciiCharacters>"
<< std::endl;
xout << " <AllowSpaceInPath>true</AllowSpaceInPath>" << std::endl;
}
else
{
if(!AllowNonAsciiCharacters.empty())
{
xout << " <AllowNonAsciiCharacters>" << AllowNonAsciiCharacters
<< "</AllowNonAsciiCharacters>" << std::endl;
}
if(!AllowSpaceInPath.empty())
{
xout << " <AllowAllowSpaceInPath>" << AllowSpaceInPath
<< "</AllowSpaceInPath>" << std::endl;
}
}
// Control script (copy to config dir)
if(!IsVersionLess("2.0") && !ControlScript.empty())
{
std::string name = cmSystemTools::GetFilenameName(ControlScript);
std::string path = Directory + "/config/" + name;
cmsys::SystemTools::CopyFileIfDifferent(ControlScript.data(), path.data());
xout << " <ControlScript>" << name << "</ControlScript>" << std::endl;
}
xout << "</Installer>" << std::endl;
}
......@@ -402,5 +532,10 @@ void cmCPackIFWInstaller::GeneratePackageFiles()
{
cmCPackIFWPackage* package = pit->second;
package->GeneratePackageFile();
}
}
}
void cmCPackIFWInstaller::WriteGeneratedByToStrim(cmGeneratedFileStream &xout)
{
if(Generator) Generator->WriteGeneratedByToStrim(xout);
}
......@@ -13,6 +13,7 @@
#ifndef cmCPackIFWInstaller_h
#define cmCPackIFWInstaller_h
#include <cmGeneratedFileStream.h>
#include <cmStandardIncludes.h>
class cmCPackIFWPackage;
......@@ -69,17 +70,39 @@ public: // Configuration
/// Filename for a logo
std::string Logo;
/// Name of the default program group in the Windows Start menu
std::string StartMenuDir;
/// Default target directory for installation
std::string TargetDir;
/// Default target directory for installation with administrator rights
std::string AdminTargetDir;
/// Filename of the generated maintenance tool
std::string MaintenanceToolName;
/// Filename for the configuration of the generated maintenance tool
std::string MaintenanceToolIniFile;
/// Set to true if the installation path can contain non-ASCII characters
std::string AllowNonAsciiCharacters;
/// Set to false if the installation path cannot contain space characters
std::string AllowSpaceInPath;
/// Filename for a custom installer control script
std::string ControlScript;
public: // Internal implementation
const char* GetOption(const std::string& op) const;
bool IsOn(const std::string& op) const;
bool IsVersionLess(const char *version);
bool IsVersionGreater(const char *version);
bool IsVersionEqual(const char *version);
void ConfigureFromOptions();
void GenerateInstallerFile();
......@@ -90,6 +113,9 @@ public: // Internal implementation
PackagesMap Packages;
std::vector<RepositoryStruct> Repositories;
std::string Directory;
protected:
void WriteGeneratedByToStrim(cmGeneratedFileStream& xout);
};
#endif // cmCPackIFWInstaller_h
......@@ -131,6 +131,24 @@ bool cmCPackIFWPackage::IsOn(const std::string &op) const
return Generator ? Generator->IsOn(op) : false;
}
//----------------------------------------------------------------------------
bool cmCPackIFWPackage::IsVersionLess(const char *version)
{
return Generator ? Generator->IsVersionLess(version) : false;
}
//----------------------------------------------------------------------------
bool cmCPackIFWPackage::IsVersionGreater(const char *version)
{
return Generator ? Generator->IsVersionGreater(version) : false;
}
//----------------------------------------------------------------------------
bool cmCPackIFWPackage::IsVersionEqual(const char *version)
{
return Generator ? Generator->IsVersionEqual(version) : false;
}
//----------------------------------------------------------------------------
std::string cmCPackIFWPackage::GetComponentName(cmCPackComponent *component)
{
......@@ -432,6 +450,9 @@ void cmCPackIFWPackage::GeneratePackageFile()
cmGeneratedFileStream xout((Directory + "/meta/package.xml").data());
xout << "<?xml version=\"1.0\"?>" << std::endl;
WriteGeneratedByToStrim(xout);
xout << "<Package>" << std::endl;
xout << " <DisplayName>" << DisplayName
......@@ -538,3 +559,8 @@ void cmCPackIFWPackage::GeneratePackageFile()
xout << "</Package>" << std::endl;
}
void cmCPackIFWPackage::WriteGeneratedByToStrim(cmGeneratedFileStream &xout)
{
if(Generator) Generator->WriteGeneratedByToStrim(xout);
}
......@@ -14,6 +14,7 @@
#define cmCPackIFWPackage_h
#include <cmStandardIncludes.h>
#include <cmGeneratedFileStream.h>
class cmCPackComponent;
class cmCPackComponentGroup;
......@@ -107,6 +108,10 @@ public: // Internal implementation
const char* GetOption(const std::string& op) const;
bool IsOn(const std::string& op) const;
bool IsVersionLess(const char *version);
bool IsVersionGreater(const char *version);
bool IsVersionEqual(const char *version);
std::string GetComponentName(cmCPackComponent *component);
void DefaultConfiguration();
......@@ -128,6 +133,9 @@ public: // Internal implementation
std::set<DependenceStruct*> AlienDependencies;
// Patch to package directory
std::string Directory;
protected:
void WriteGeneratedByToStrim(cmGeneratedFileStream& xout);
};
#endif // cmCPackIFWPackage_h
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