mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
Rewrite translation file generation using cmake
This makes three big changes to how translation files are generated: - use Qt5 cmake built-in commands to do the translations rather than calling lrelease directly. lrelease is often not in the path, while Qt5 cmake knows how to find and invoke it. - Slam the resulting files into a C++ file using a cmake script rather than needing to compile a .c file to generate C++ file. This is simpler, but more importantly avoids the mess needed when cross compiling of having to import a cmake script from an external native build. - In the actual generated files, use an unordered_map rather than a massive list of static variable pointers.
This commit is contained in:
parent
1c1a7e6f84
commit
da400f6d66
10 changed files with 56 additions and 152 deletions
|
@ -1,7 +1,7 @@
|
|||
local default_deps_base='libsystemd-dev libboost-filesystem-dev libboost-thread-dev libboost-date-time-dev libboost-chrono-dev libgtest-dev ' +
|
||||
'libboost-regex-dev libboost-serialization-dev libboost-program-options-dev libunbound-dev nettle-dev libevent-dev libminiupnpc-dev ' +
|
||||
'libunwind8-dev libsodium-dev libssl-dev libreadline-dev libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler python3 ' +
|
||||
'pkg-config libsqlite3-dev';
|
||||
'pkg-config libsqlite3-dev qttools5-dev';
|
||||
local default_deps='g++ ' + default_deps_base; // g++ sometimes needs replacement
|
||||
|
||||
local gtest_filter='-AddressFromURL.Failure:DNSResolver.DNSSEC*:is_hdd.linux_os_root';
|
||||
|
|
|
@ -433,8 +433,6 @@ add_definition_if_library_exists(c explicit_bzero "strings.h" HAVE_EXPLICIT_BZER
|
|||
add_definition_if_function_found(strptime HAVE_STRPTIME)
|
||||
|
||||
# Generate header for embedded translations
|
||||
# Generate header for embedded translations, use target toolchain if depends, otherwise use the
|
||||
# lrelease and lupdate binaries from the host
|
||||
add_subdirectory(translations)
|
||||
|
||||
add_library(miniupnpc INTERFACE)
|
||||
|
|
|
@ -59,7 +59,7 @@ library archives (`.a`).
|
|||
| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | YES | XML parsing |
|
||||
| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation |
|
||||
| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation |
|
||||
| lrelease | ? | NO | `qttools5-dev-tools` | `qt5-tools` | `qt5-linguist` | YES | Translations |
|
||||
| Qt tools | 5.x | NO | `qttools5-dev` | `qt5-tools` | `qt5-linguist` | YES | Translations |
|
||||
| libhidapi | ? | NO | `libhidapi-dev` | `hidapi` | `hidapi-devel` | YES | Hardware wallet |
|
||||
| libusb | ? | NO | `libusb-dev` | `libusb` | `libusb-devel` | YES | Hardware wallet |
|
||||
| libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | YES | Hardware wallet |
|
||||
|
@ -70,7 +70,7 @@ build the library binary manually. This can be done with the following command `
|
|||
|
||||
Install all dependencies at once on Debian/Ubuntu:
|
||||
|
||||
``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev libsqlite3-dev qttools5-dev-tools libhidapi-dev libusb-dev libprotobuf-dev protobuf-compiler ```
|
||||
``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev libsqlite3-dev qttools5-dev libhidapi-dev libusb-dev libprotobuf-dev protobuf-compiler ```
|
||||
|
||||
Install all dependencies at once on macOS with the provided Brewfile:
|
||||
``` brew update && brew bundle --file=contrib/brew/Brewfile ```
|
||||
|
|
|
@ -51,8 +51,13 @@ add_library(common
|
|||
timings.cc
|
||||
updates.cpp
|
||||
util.cpp
|
||||
${PROJECT_BINARY_DIR}/translations/translation_files.cpp
|
||||
)
|
||||
|
||||
set_source_files_properties(${PROJECT_BINARY_DIR}/translations/translation_files.cpp PROPERTIES GENERATED 1)
|
||||
add_dependencies(common generate_translation_data)
|
||||
|
||||
|
||||
if (STACK_TRACE)
|
||||
target_sources(common PRIVATE stack_trace.cpp)
|
||||
if(WIN32 OR STATIC)
|
||||
|
@ -64,8 +69,6 @@ if (BACKCOMPAT)
|
|||
target_sources(common PRIVATE compat/glibc_compat.cpp)
|
||||
endif()
|
||||
|
||||
add_dependencies(common generate_translations_header)
|
||||
|
||||
target_link_libraries(common
|
||||
PUBLIC
|
||||
cncrypto
|
||||
|
@ -77,8 +80,6 @@ target_link_libraries(common
|
|||
OpenSSL::Crypto
|
||||
extra)
|
||||
|
||||
target_include_directories(common PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../../translations")
|
||||
|
||||
option(STACK_TRACE "Install a hook that dumps stack on exception" OFF)
|
||||
|
||||
if(STACK_TRACE)
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include <map>
|
||||
#include "file_io_utils.h"
|
||||
#include "common/i18n.h"
|
||||
#include "translation_files.h"
|
||||
|
||||
#undef LOKI_DEFAULT_LOG_CATEGORY
|
||||
#define LOKI_DEFAULT_LOG_CATEGORY "i18n"
|
||||
|
|
|
@ -34,5 +34,5 @@
|
|||
std::string i18n_get_language();
|
||||
int i18n_set_language(const char *directory, const char *base, std::string language = std::string());
|
||||
const char *i18n_translate(const char *str, const std::string &context);
|
||||
static inline std::string get_default_i18n_context() { return std::string(); }
|
||||
static inline const char *tr(const char *str) { return i18n_translate(str,get_default_i18n_context()); }
|
||||
inline const char *tr(const char *str) { return i18n_translate(str, std::string{}); }
|
||||
bool find_embedded_file(const std::string &name, std::string &data); // In the generated translation_files.cpp
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2017-2018, The Monero Project
|
||||
# Copyright (c) 2020, The Loki Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -26,58 +26,24 @@
|
|||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
project(translations)
|
||||
|
||||
# when crosscompiling import the executable targets from a file
|
||||
IF(CMAKE_CROSSCOMPILING)
|
||||
message(WARNING "CrossCompiling")
|
||||
SET(IMPORT_EXECUTABLES "${CMAKE_CURRENT_BINARY_DIR}/ImportExecutables.cmake" CACHE FILEPATH "Point it to the export file from a native build")
|
||||
INCLUDE(${IMPORT_EXECUTABLES})
|
||||
ENDIF(CMAKE_CROSSCOMPILING)
|
||||
|
||||
# only build the generator if not crosscompiling
|
||||
IF(NOT CMAKE_CROSSCOMPILING)
|
||||
add_executable(generate_translations_header generate_translations_header.c)
|
||||
ENDIF(NOT CMAKE_CROSSCOMPILING)
|
||||
|
||||
if(NOT LRELEASE_PATH OR LRELEASE_PATH STREQUAL "")
|
||||
find_program(LRELEASE lrelease)
|
||||
find_package(Qt5 QUIET COMPONENTS Core LinguistTools)
|
||||
if(NOT Qt5_FOUND OR NOT Qt5LinguistTools_FOUND)
|
||||
set(qm_files "")
|
||||
message(WARNING "Qt5 LingustTools not found, translation files not built")
|
||||
else()
|
||||
set(LRELEASE ${LRELEASE_PATH}/lrelease)
|
||||
file(GLOB ts_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" *.ts)
|
||||
qt5_add_translation(qm_files ${ts_files})
|
||||
endif()
|
||||
|
||||
if(LRELEASE STREQUAL "LRELEASE-NOTFOUND")
|
||||
set(ts_files "")
|
||||
message(WARNING "lrelease program not found, translation files not built")
|
||||
else()
|
||||
execute_process(COMMAND ${LRELEASE} -version
|
||||
RESULT_VARIABLE lrelease_ret)
|
||||
if(NOT lrelease_ret EQUAL "0")
|
||||
set(ts_files "")
|
||||
message(WARNING "lrelease program not working, translation files not built")
|
||||
else()
|
||||
file(GLOB ts_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" *.ts)
|
||||
foreach(ts_file ${ts_files})
|
||||
string(REPLACE ".ts" ".qm" qm_file "${ts_file}")
|
||||
add_custom_command(TARGET generate_translations_header
|
||||
PRE_BUILD
|
||||
COMMAND ${LRELEASE} "${CMAKE_CURRENT_SOURCE_DIR}/${ts_file}" -qm "${qm_file}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(REPLACE ".ts" ".qm" qm_files "${ts_files}")
|
||||
|
||||
add_custom_command(TARGET generate_translations_header
|
||||
POST_BUILD
|
||||
COMMAND ./generate_translations_header ${qm_files}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}"
|
||||
COMMENT "Generating embedded translations header")
|
||||
|
||||
# export the generator target to a file, so it can be imported (see above) by another build
|
||||
IF(NOT CMAKE_CROSSCOMPILING)
|
||||
EXPORT(TARGETS generate_translations_header FILE ${CMAKE_CURRENT_BINARY_DIR}/ImportExecutables.cmake )
|
||||
ENDIF(NOT CMAKE_CROSSCOMPILING)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/translation_files.cpp
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D "qm_files=${qm_files}"
|
||||
-D "base_dir=${CMAKE_CURRENT_BINARY_DIR}"
|
||||
-D "in_file=${CMAKE_CURRENT_SOURCE_DIR}/translation_files.cpp.in"
|
||||
-D "out_file=${CMAKE_CURRENT_BINARY_DIR}/translation_files.cpp"
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/generate_translation_data.cmake
|
||||
DEPENDS ${qm_files} ${CMAKE_CURRENT_SOURCE_DIR}/generate_translation_data.cmake
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(generate_translation_data DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/translation_files.cpp)
|
||||
|
|
10
translations/generate_translation_data.cmake
Normal file
10
translations/generate_translation_data.cmake
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
set(TRANSLATION_FILES "")
|
||||
foreach(qm_file ${qm_files})
|
||||
file(READ "${qm_file}" trans_data HEX)
|
||||
file(RELATIVE_PATH basename "${base_dir}" "${qm_file}")
|
||||
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "\\\\x\\1" trans_data "${trans_data}")
|
||||
set(TRANSLATION_FILES "${TRANSLATION_FILES} {\"${basename}\"s, \"${trans_data}\"s},\n")
|
||||
endforeach()
|
||||
|
||||
configure_file(${in_file} ${out_file} @ONLY)
|
|
@ -1,87 +0,0 @@
|
|||
// Copyright (c) 2013, Sergey Lyubka
|
||||
// Copyright (c) 2017-2018, The Monero Project
|
||||
// All rights reserved.
|
||||
// Released under the MIT license.
|
||||
|
||||
// This program takes a list of files as an input, and produces C++ code that
|
||||
// contains the contents of all these files as a collection of strings.
|
||||
//
|
||||
// Usage:
|
||||
// 1. Compile this file:
|
||||
// cc -o generate-translations-header generate-translations-header.c
|
||||
//
|
||||
// 2. Convert list of files into single header:
|
||||
// ./generate-translations-header monero_fr.qm monero_it.qm > translations_files.h
|
||||
//
|
||||
// 3. In your application code, include translations_files.h, then you can
|
||||
// access the files using this function:
|
||||
// static bool find_embedded_file(const std::string &file_name, std::string &data);
|
||||
// std::string data;
|
||||
// find_embedded_file("monero_fr.qm", data);
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static const char *code =
|
||||
"static bool find_embedded_file(const std::string &name, std::string &data) {\n"
|
||||
" const struct embedded_file *p;\n"
|
||||
" for (p = embedded_files; p->name != NULL; p++) {\n"
|
||||
" if (*p->name == name) {\n"
|
||||
" data = *p->data;\n"
|
||||
" return true;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" return false;\n"
|
||||
"}\n";
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
FILE *fp, *foutput;
|
||||
int i, j, ch;
|
||||
|
||||
if((foutput = fopen("translation_files.h", "w")) == NULL) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fprintf(foutput, "#ifndef TRANSLATION_FILES_H\n");
|
||||
fprintf(foutput, "#define TRANSLATION_FILES_H\n\n");
|
||||
fprintf(foutput, "#include <string>\n\n");
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if ((fp = fopen(argv[i], "rb")) == NULL) {
|
||||
fclose(foutput);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(foutput, "static const std::string translation_file_name_%d = \"%s\";\n", i, argv[i]);
|
||||
fprintf(foutput, "static const std::string translation_file_data_%d = std::string(", i);
|
||||
for (j = 0; (ch = fgetc(fp)) != EOF; j++) {
|
||||
if ((j % 16) == 0) {
|
||||
if (j > 0) {
|
||||
fprintf(foutput, "%s", "\"");
|
||||
}
|
||||
fprintf(foutput, "%s", "\n \"");
|
||||
}
|
||||
fprintf(foutput, "\\x%02x", ch);
|
||||
}
|
||||
fprintf(foutput, "\",\n %d);\n\n", j);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(foutput, "%s", "static const struct embedded_file {\n");
|
||||
fprintf(foutput, "%s", " const std::string *name;\n");
|
||||
fprintf(foutput, "%s", " const std::string *data;\n");
|
||||
fprintf(foutput, "%s", "} embedded_files[] = {\n");
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
fprintf(foutput, " {&translation_file_name_%d, &translation_file_data_%d},\n", i, i);
|
||||
}
|
||||
fprintf(foutput, "%s", " {NULL, NULL}\n");
|
||||
fprintf(foutput, "%s", "};\n\n");
|
||||
fprintf(foutput, "%s\n", code);
|
||||
|
||||
fprintf(foutput, "#endif /* TRANSLATION_FILES_H */\n");
|
||||
|
||||
fclose(foutput);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
17
translations/translation_files.cpp.in
Normal file
17
translations/translation_files.cpp.in
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
static std::unordered_map<std::string, std::string> _translation_files{
|
||||
@TRANSLATION_FILES@
|
||||
};
|
||||
|
||||
bool find_embedded_file(const std::string &name, std::string &data) {
|
||||
auto it = _translation_files.find(name);
|
||||
if (it != _translation_files.end()) {
|
||||
data = it->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
Loading…
Reference in a new issue