Hi Brad, the attached patch limits lines to 79 cols, and adds the necessary documentation to the variables and release notes.
I added diagnostic pragmas to suppress the deprecation warnings as well as a comment explaining the need for them. I've looked for a replacement some more, but I still haven't been able to find anything. I could try to hard-code it by writing a simple program that iterates all possible language codes (it's "only" a 16-bit value, so 2^16 combinations). However, this feels even hackier to me than the current state. This patch is a single commit rebased on current master (60cbd9b9da2059481e2f29fbb5859a5b0643d3d7) Kind regards, Simon On Mi, 2015-11-04 at 10:46 -0500, Brad King wrote: > On 11/03/2015 06:09 AM, Levermann, Simon wrote: > > This adds support for multilingual SLAs which are displayed when > > the user is trying to mount the DMG. > > Thanks for working on this and for bringing the patch to this list. > > Patches 2 and 3 look like fixups. Please squash that all into one > commit. Also please keep C++ sources wrapped to 79 columns or less. > > > Multiple languages can be added via the new variables > > CPACK_DMG_SLA_DIR > > and CPACK_DMG_SLA_LANGUAGES. > > Please add Help/variable/*.rst files to document these. Also > please add a Help/release/dev/*.rst file to add a release note > for the feature. > > > For each language defined, CPack will search for a > > language.menu.txt > > and language.license.txt file in CPACK_SLA_DIR. > > Good. Please mention this in the above-requested documentation. > > > This patch adds a library to the deprecated Carbon Framework to > > CPackLib, since the functions/types required to acquire the region > > code for the internal LPic data structure are only available in > > this > > old API. Apple does not seem to be offering a replacement API for > > the > > old ScriptManager region codes. > > Hopefully an alternative can be found. Meanwhile I get warnings > during the build due to DEPRECATED_IN_MAC_OS_X_VERSION_10_6_AND_LATER > appearing on the declarations of these APIs. Please add pragmas > or whatever is needed to suppress them, at least with Clang. Call > out the purpose for the suppression with a comment explaining why > we need to use the deprecated APIs. > > > Additional thought: One could add the language.menu.txt files for > > some common languages to the repository. > > Let's defer that until the actual feature is working. > > Thanks, > -Brad >
From 68bd81a62f7c4a11cb0bc46a64d6b0da0a8a6688 Mon Sep 17 00:00:00 2001 From: Simon Levermann <simon-git...@slevermann.de> Date: Mon, 19 Oct 2015 11:13:55 +0200 Subject: [PATCH] Add support for multilingual SLAs Multiple languages for SLAs and the SLA UI can be added via the CPack variables CPACK_DMG_SLA_DIR and CPACK_DMG_SLA_LANGUAGES. For each language defined in the languages variable, CPack will search for <language>.menu.txt and <language>.license.txt in CPACK_DMG_SLA_DIR. If the sla directory variable is not defined, the old behaviour using CPACK_RESOURCE_FILE_LICENSE is retained Pass string by const& instead of copying Remove superfluous assignment Add deprecation pragma and comment Break lines longer than 79 characters Add variable documentation for new CPACK_DMG_ variables Add release note for multilanguage-sla --- Help/release/dev/cpack-dmg-multilanguage-sla.txt | 6 + Help/variable/CPACK_DMG_SLA_DIR | 6 + Help/variable/CPACK_DMG_SLA_LANGUAGES | 6 + Source/CMakeLists.txt | 4 + Source/CPack/cmCPackDragNDropGenerator.cxx | 357 ++++++++++++++++++++--- Source/CPack/cmCPackDragNDropGenerator.h | 10 + 6 files changed, 351 insertions(+), 38 deletions(-) create mode 100644 Help/release/dev/cpack-dmg-multilanguage-sla.txt create mode 100644 Help/variable/CPACK_DMG_SLA_DIR create mode 100644 Help/variable/CPACK_DMG_SLA_LANGUAGES diff --git a/Help/release/dev/cpack-dmg-multilanguage-sla.txt b/Help/release/dev/cpack-dmg-multilanguage-sla.txt new file mode 100644 index 0000000..cdf3162 --- /dev/null +++ b/Help/release/dev/cpack-dmg-multilanguage-sla.txt @@ -0,0 +1,6 @@ +cpack-dmg-multilanguage-sla +-------------- + +* This feature adds the capability to add multi-lingual SLAs to a DMG which + is presented to the user when they try to mount the DMG. See :variable:`CPACK_DMG_SLA_LANGUAGES` + and :variable:`CPACK_DMG_SLA_DIR` for usage information. diff --git a/Help/variable/CPACK_DMG_SLA_DIR b/Help/variable/CPACK_DMG_SLA_DIR new file mode 100644 index 0000000..7b38a23 --- /dev/null +++ b/Help/variable/CPACK_DMG_SLA_DIR @@ -0,0 +1,6 @@ +CPACK_DMG_SLA_DIR +-------------------- + +Directory where license and menu files for different languages are stored + +See :variable:`CPACK_DMG_SLA_LANGUAGES` for more information. diff --git a/Help/variable/CPACK_DMG_SLA_LANGUAGES b/Help/variable/CPACK_DMG_SLA_LANGUAGES new file mode 100644 index 0000000..77eef61 --- /dev/null +++ b/Help/variable/CPACK_DMG_SLA_LANGUAGES @@ -0,0 +1,6 @@ +CPACK_DMG_SLA_LANGUAGES +-------------------- + +Languages for which a license agreement is provided when mounting the generated DMG. + +For every language in this list, CPack will try to find a file language.menu.txt and language.license.txt in :variable:`CPACK_DMG_SLA_DIR`. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index fd71b0e..729f6ab 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -726,6 +726,10 @@ endif() # Build CPackLib add_library(CPackLib ${CPACK_SRCS}) target_link_libraries(CPackLib CMakeLib) +if(APPLE) + include_directories (${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/Carbon.framework/Headers/) + target_link_libraries(CPackLib "-framework Carbon") +endif() if(APPLE) add_executable(cmakexbuild cmakexbuild.cxx) diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx index 4c400d9..993a571 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.cxx +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx @@ -18,6 +18,20 @@ #include <cmsys/RegularExpression.hxx> #include <cmsys/FStream.hxx> +#include <iomanip> + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CFString.h> +#include <CoreFoundation/CFLocale.h> + +// The carbon framework is deprecated, but the Region codes it supplies are +// needed for the LPic data structure used for generating multi-lingual SLAs. +// There does not seem to be a replacement API for these region codes. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#include <Carbon.h> +#pragma clang diagnostic pop + static const char* SLAHeader = "data 'LPic' (5000) {\n" " $\"0002 0011 0003 0001 0000 0000 0002 0000\"\n" @@ -103,6 +117,70 @@ int cmCPackDragNDropGenerator::InitializeInternal() } this->SetOptionIfNotSet("CPACK_COMMAND_REZ", rez_path.c_str()); + if(this->IsSet("CPACK_DMG_SLA_DIR")) + { + slaDirectory = this->GetOption("CPACK_DMG_SLA_DIR"); + if(!slaDirectory.empty() && this->IsSet("CPACK_RESOURCE_FILE_LICENSE")) + { + std::string license_file = + this->GetOption("CPACK_RESOURCE_FILE_LICENSE"); + if(!license_file.empty() && + (license_file.find("CPack.GenericLicense.txt") == std::string::npos)) + { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "Both CPACK_DMG_SLA_DIR and CPACK_RESOURCE_FILE_LICENSE specified, " + "defaulting to CPACK_DMG_SLA_DIR" + << std::endl); + } + } + if(!this->IsSet("CPACK_DMG_LANGUAGES")) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_DMG_SLA_DIR set but no languages defined " + "(set CPACK_DMG_LANGUAGES)" + << std::endl); + return 0; + } + if(!cmSystemTools::FileExists(slaDirectory, false)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_DMG_SLA_DIR does not exist" + << std::endl); + return 0; + } + + std::vector<std::string> languages; + cmSystemTools::ExpandListArgument(this->GetOption("CPACK_DMG_LANGUAGES"), + languages); + if(languages.empty()) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_DMG_LANGUAGES set but empty" + << std::endl); + return 0; + } + for(size_t i = 0; i < languages.size(); ++i) + { + std::string license = slaDirectory + "/" + languages[i] + + ".license.txt"; + if (!cmSystemTools::FileExists(license)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Missing license file " << languages[i] << ".license.txt" + << std::endl); + return 0; + } + std::string menu = slaDirectory + "/" + languages[i] + ".menu.txt"; + if (!cmSystemTools::FileExists(menu)) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Missing menu file " << languages[i] << ".menu.txt" + << std::endl); + return 0; + } + } + } + return this->Superclass::InitializeInternal(); } @@ -246,9 +324,21 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, this->GetOption("CPACK_DMG_DS_STORE") ? this->GetOption("CPACK_DMG_DS_STORE") : ""; + const std::string cpack_dmg_languages = + this->GetOption("CPACK_DMG_LANGUAGES") + ? this->GetOption("CPACK_DMG_LANGUAGES") : ""; + // only put license on dmg if is user provided if(!cpack_license_file.empty() && - cpack_license_file.find("CPack.GenericLicense.txt") != std::string::npos) + cpack_license_file.find("CPack.GenericLicense.txt") + != std::string::npos) + { + cpack_license_file = ""; + } + + // use sla_dir if both sla_dir and license_file are set + if(!cpack_license_file.empty() && + !slaDirectory.empty()) { cpack_license_file = ""; } @@ -418,55 +508,123 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, } } - if(!cpack_license_file.empty()) + if(!cpack_license_file.empty() || !slaDirectory.empty()) { + // Use old hardcoded style if sla_dir is not set + bool oldStyle = slaDirectory.empty(); std::string sla_r = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); sla_r += "/sla.r"; - cmsys::ifstream ifs; - ifs.open(cpack_license_file.c_str()); - if(ifs.is_open()) + std::vector<std::string> languages; + if(!oldStyle) { - cmGeneratedFileStream osf(sla_r.c_str()); - osf << "#include <CoreServices/CoreServices.r>\n\n"; - osf << SLAHeader; - osf << "\n"; - osf << "data 'TEXT' (5002, \"English\") {\n"; - while(ifs.good()) + cmSystemTools::ExpandListArgument(cpack_dmg_languages, languages); + } + + cmGeneratedFileStream ofs(sla_r.c_str()); + ofs << "#include <CoreServices/CoreServices.r>\n\n"; + if(oldStyle) + { + ofs << SLAHeader; + ofs << "\n"; + } + else + { + /* + * LPic Layout + * (https://github.com/pypt/dmg-add-license/blob/master/main.c) + * as far as I can tell (no official documentation seems to exist): + * struct LPic { + * uint16_t default_language; // points to a resid, defaulting to 0, + * // which is the first set language + * uint16_t length; + * struct { + * uint16_t language_code; + * uint16_t resid; + * uint16_t encoding; // Encoding from TextCommon.h, + * // forcing MacRoman (0) for now. Might need to + * // allow overwrite per license by user later + * } item[1]; + * } + */ + + // Create vector first for readability, then iterate to write to ofs + std::vector<std::uint16_t> header_data; + header_data.push_back(0); + header_data.push_back(languages.size()); + for(size_t i = 0; i < languages.size(); ++i) { - std::string line; - std::getline(ifs, line); - // escape quotes - std::string::size_type pos = line.find('\"'); - while(pos != std::string::npos) + CFStringRef language_cfstring = CFStringCreateWithCString(NULL, + languages[i].c_str(), kCFStringEncodingUTF8); + CFStringRef iso_language = + CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, + language_cfstring); + if (!iso_language) { - line.replace(pos, 1, "\\\""); - pos = line.find('\"', pos+2); + cmCPackLogger(cmCPackLog::LOG_ERROR, + languages[i] << " is not a recognized language" + << std::endl); } - // break up long lines to avoid Rez errors - std::vector<std::string> lines; - const size_t max_line_length = 512; - for(size_t i=0; i<line.size(); i+= max_line_length) - { - int line_length = max_line_length; - if(i+max_line_length > line.size()) - line_length = line.size()-i; - lines.push_back(line.substr(i, line_length)); - } + char *iso_language_cstr = (char *) malloc(65); + CFStringGetCString(iso_language, iso_language_cstr, 64, + kCFStringEncodingMacRoman); + LangCode lang = 0; + RegionCode region = 0; + OSStatus err = LocaleStringToLangAndRegionCodes(iso_language_cstr, + &lang, ®ion); + if (err != noErr) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "No language/region code available for " << iso_language_cstr + << std::endl); + free(iso_language_cstr); + return 0; + } + free(iso_language_cstr); + header_data.push_back(region); + header_data.push_back(i); + header_data.push_back(0); + } + ofs << "data 'LPic' (5000) {\n"; + ofs << std::hex << std::uppercase << std::setfill('0'); - for(size_t i=0; i<lines.size(); i++) - { - osf << " \"" << lines[i] << "\"\n"; - } - osf << " \"\\n\"\n"; + for(size_t i = 0; i < header_data.size(); ++i) + { + if(i % 8 == 0) + { + ofs << " $\""; + } + + ofs << std::setw(4) << header_data[i]; + + if(i % 8 == 7 || i == header_data.size() - 1) + { + ofs << "\"\n"; + } + else + { + ofs << " "; + } + } + ofs << "};\n\n"; + // Reset ofs options + ofs << std::dec << std::nouppercase << std::setfill(' '); + } + + if(oldStyle) + { + WriteLicense(ofs, 0, "", cpack_license_file); + } + else + { + for(size_t i = 0; i < languages.size(); ++i) + { + WriteLicense(ofs, i + 5000, languages[i]); } - osf << "};\n"; - osf << "\n"; - osf << SLASTREnglish; - ifs.close(); - osf.close(); } + ofs.Close(); + // convert to UDCO std::string temp_udco = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); temp_udco += "/temp-udco.dmg"; @@ -607,3 +765,126 @@ cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix( return GetComponentPackageFileName(package_file_name, componentName, false); } + +void +cmCPackDragNDropGenerator::WriteLicense(cmGeneratedFileStream& outputStream, + int licenseNumber, std::string licenseLanguage, std::string licenseFile) +{ + if(!licenseFile.empty()) + { + licenseNumber = 5002; + licenseLanguage = "English"; + } + + // License header + outputStream << "data 'TEXT' (" << licenseNumber << ", \"" + << licenseLanguage << "\") {\n"; + // License body + std::string actual_license = !licenseFile.empty() ? licenseFile : + (slaDirectory + "/" + licenseLanguage + ".license.txt"); + cmsys::ifstream license_ifs; + license_ifs.open(actual_license); + if(license_ifs.is_open()) + { + while(license_ifs.good()) + { + std::string line; + std::getline(license_ifs, line); + if(!line.empty()) + { + EscapeQuotes(line); + std::vector<std::string> lines; + BreakLongLine(line, lines); + for(size_t i = 0; i < lines.size(); ++i) + { + outputStream << " \"" << lines[i] << "\"\n"; + } + } + outputStream << " \"\\n\"\n"; + } + license_ifs.close(); + } + + // End of License + outputStream << "};\n\n"; + if(!licenseFile.empty()) + { + outputStream << SLASTREnglish; + } + else + { + // Menu header + outputStream << "resource 'STR#' (" << licenseNumber << ", \"" + << licenseLanguage << "\") {\n"; + outputStream << " {\n"; + + // Menu body + cmsys::ifstream menu_ifs; + menu_ifs.open(slaDirectory + "/" + licenseLanguage + ".menu.txt"); + if(menu_ifs.is_open()) + { + size_t lines_written = 0; + while(menu_ifs.good()) + { + // Lines written from original file, not from broken up lines + std::string line; + std::getline(menu_ifs, line); + if(!line.empty()) + { + EscapeQuotes(line); + std::vector<std::string> lines; + BreakLongLine(line, lines); + for(size_t i = 0; i < lines.size(); ++i) + { + std::string comma; + // We need a comma after every complete string, + // but not on the very last line + if(lines_written != 8 && i == lines.size() - 1) + { + comma = ","; + } + else + { + comma = ""; + } + outputStream << " \"" << lines[i] << "\"" << comma << "\n"; + } + ++lines_written; + } + } + menu_ifs.close(); + } + + //End of menu + outputStream << " }\n"; + outputStream << "};\n"; + outputStream << "\n"; + } +} + +void +cmCPackDragNDropGenerator::BreakLongLine(const std::string& line, + std::vector<std::string>& lines) +{ + const size_t max_line_length = 512; + for(size_t i = 0; i < line.size(); i += max_line_length) + { + int line_length = max_line_length; + if(i + max_line_length > line.size()) + { + line_length = line.size() - i; + } + lines.push_back(line.substr(i, line_length)); + } +} + +void +cmCPackDragNDropGenerator::EscapeQuotes(std::string& line) +{ + std::string::size_type pos = line.find('\"'); + while(pos != std::string::npos) + { + line.replace(pos, 1, "\\\""); + pos = line.find('\"', pos + 2); + } +} diff --git a/Source/CPack/cmCPackDragNDropGenerator.h b/Source/CPack/cmCPackDragNDropGenerator.h index 1c84d49..da016f6 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.h +++ b/Source/CPack/cmCPackDragNDropGenerator.h @@ -14,6 +14,7 @@ #define cmCPackDragNDropGenerator_h #include "cmCPackGenerator.h" +#include "cmGeneratedFileStream.h" /** \class cmCPackDragNDropGenerator * \brief A generator for OSX drag-n-drop installs @@ -42,6 +43,15 @@ protected: int CreateDMG(const std::string& src_dir, const std::string& output_file); std::string InstallPrefix; + +private: + std::string slaDirectory; + + void WriteLicense(cmGeneratedFileStream& outputStream, int licenseNumber, + std::string licenseLanguage, std::string licenseFile = ""); + void BreakLongLine(const std::string& line, + std::vector<std::string>& lines); + void EscapeQuotes(std::string& line); }; #endif -- 2.5.0
-- Powered by www.kitware.com Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ Kitware offers various services to support the CMake community. For more information on each offering, please visit: CMake Support: http://cmake.org/cmake/help/support.html CMake Consulting: http://cmake.org/cmake/help/consulting.html CMake Training Courses: http://cmake.org/cmake/help/training.html Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html Follow this link to subscribe/unsubscribe: http://public.kitware.com/mailman/listinfo/cmake-developers