diff --git a/scripts/meson_configure_qmake_in.py b/scripts/meson_configure_qmake_in.py new file mode 100644 index 00000000..33763465 --- /dev/null +++ b/scripts/meson_configure_qmake_in.py @@ -0,0 +1,26 @@ + +import sys +import re +import json + +in_filename = sys.argv[1] +out_filename = sys.argv[2] +vars = json.loads(sys.argv[3]) + +with open(in_filename, "r") as f: + content = f.read() + +content = content.replace("\\\"", "\"") + +varname_pattern = re.compile("[A-Za-z0-9_]+") +for name, value in vars.items(): + if varname_pattern.fullmatch(name) is None: + print("Name \"{}\" is not a valid variable name.".format(name)) + continue + + pattern = "\\$\\$({}|\\{{{}\\}})".format(name, name) + print(pattern) + content = re.sub(pattern, re.escape(str(value)), content) + +with open(out_filename, "w") as f: + f.write(content) diff --git a/scripts/meson_parse_qmake.py b/scripts/meson_parse_qmake.py index 2141dec3..39ad8b7d 100644 --- a/scripts/meson_parse_qmake.py +++ b/scripts/meson_parse_qmake.py @@ -3,8 +3,6 @@ import sys name = sys.argv[1] value = [] -if name not in ('QT', 'SOURCES', 'HEADERS', 'FORMS', 'RESOURCES', 'VERSION', 'ICON'): - sys.exit(1) root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) with open(os.path.join(root, 'src', 'Cutter.pro')) as f: text = f.read() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce029bea..843a1ce8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,12 +4,34 @@ cmake_minimum_required(VERSION 3.1) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(DisallowInSource) -project(Cutter VERSION 1.7.1) - option(CUTTER_ENABLE_JUPYTER "Enable Jupyter integration. Requires Python >= 3.3." ON) option(CUTTER_ENABLE_QTWEBENGINE "Use QtWebEngine for in-app Jupyter Browser. Unused if CUTTER_ENABLE_JUPYTER=OFF." ON) +# Parse Cutter.pro to get filenames +include(QMakeProParse) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Cutter.pro" + "${CMAKE_CURRENT_BINARY_DIR}/Cutter.pro" + COPYONLY) # trigger reconfigure if Cutter.pro changes +parse_qmake_pro("${CMAKE_CURRENT_BINARY_DIR}/Cutter.pro" CUTTER_PRO) +set(SOURCE_FILES ${CUTTER_PRO_SOURCES}) +set(HEADER_FILES ${CUTTER_PRO_HEADERS}) +set(UI_FILES ${CUTTER_PRO_FORMS}) +set(QRC_FILES ${CUTTER_PRO_RESOURCES}) +set(CUTTER_VERSION_MAJOR "${CUTTER_PRO_CUTTER_VERSION_MAJOR}") +set(CUTTER_VERSION_MINOR "${CUTTER_PRO_CUTTER_VERSION_MINOR}") +set(CUTTER_VERSION_PATCH "${CUTTER_PRO_CUTTER_VERSION_PATCH}") + +set(CUTTER_VERSION_FULL "${CUTTER_VERSION_MAJOR}.${CUTTER_VERSION_MINOR}.${CUTTER_VERSION_PATCH}") + +message(STATUS "version from Cutter.pro: ${CUTTER_VERSION_FULL}") +message(STATUS "sources from Cutter.pro: ${SOURCE_FILES}") +message(STATUS "headers from Cutter.pro: ${HEADER_FILES}") +message(STATUS "forms from Cutter.pro: ${UI_FILES}") +message(STATUS "resources from Cutter.pro: ${QRC_FILES}") + +project(Cutter VERSION "${CUTTER_VERSION_FULL}") + set(CMAKE_CXX_STANDARD 11) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -64,27 +86,13 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" endif() -# Parse Cutter.pro to get filenames -include(QMakeProParse) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Cutter.pro" - "${CMAKE_CURRENT_BINARY_DIR}/Cutter.pro" - COPYONLY) # trigger reconfigure if Cutter.pro changes -parse_qmake_pro("${CMAKE_CURRENT_BINARY_DIR}/Cutter.pro" CUTTER_PRO) -set(SOURCE_FILES ${CUTTER_PRO_SOURCES}) -set(HEADER_FILES ${CUTTER_PRO_HEADERS}) -set(UI_FILES ${CUTTER_PRO_FORMS}) -set(QRC_FILES ${CUTTER_PRO_RESOURCES}) - -message(STATUS "sources from Cutter.pro: ${SOURCE_FILES}") -message(STATUS "headers from Cutter.pro: ${HEADER_FILES}") -message(STATUS "forms from Cutter.pro: ${UI_FILES}") -message(STATUS "resources from Cutter.pro: ${QRC_FILES}") - - -set(CUTTER_VERSION_SUFFIX "-dev") -set(CUTTER_VERSION_FULL "${PROJECT_VERSION}${CUTTER_VERSION_SUFFIX}") message(STATUS "Building Cutter version ${CUTTER_VERSION_FULL}") -add_definitions("-DAPP_VERSION=\"${CUTTER_VERSION_FULL}\"") + + +include(QMakeConfigureFile) +qmake_configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CutterConfig.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/CutterConfig.h") + add_executable(Cutter ${UI_FILES} ${QRC_FILES} ${SOURCE_FILES} ${HEADER_FILES}) diff --git a/src/Cutter.h b/src/Cutter.h index 3c0b1f9a..55eb934a 100644 --- a/src/Cutter.h +++ b/src/Cutter.h @@ -26,7 +26,6 @@ #define __question(x) (QMessageBox::Yes==QMessageBox::question (this, "Alert", QString(x), QMessageBox::Yes| QMessageBox::No)) #define APPNAME "Cutter" -#define CUTTER_VERSION "1.7.1" #define Core() (CutterCore::getInstance()) diff --git a/src/Cutter.pro b/src/Cutter.pro index 4e185d3b..44a59ca4 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -2,8 +2,11 @@ TEMPLATE = app TARGET = Cutter -# The application version -VERSION = 1.7.1 +CUTTER_VERSION_MAJOR = 1 +CUTTER_VERSION_MINOR = 7 +CUTTER_VERSION_PATCH = 1 + +VERSION = $${CUTTER_VERSION_MAJOR}.$${CUTTER_VERSION_MINOR}.$${CUTTER_VERSION_PATCH} #required QT version lessThan(QT_MAJOR_VERSION, 5): error("requires Qt 5") @@ -25,8 +28,6 @@ equals(CUTTER_ENABLE_JUPYTER, true) { !defined(CUTTER_BUNDLE_R2_APPBUNDLE, var) CUTTER_BUNDLE_R2_APPBUNDLE=false equals(CUTTER_BUNDLE_R2_APPBUNDLE, true) CONFIG += CUTTER_BUNDLE_R2_APPBUNDLE -# Define the preprocessor macro to get the application version in our application. -DEFINES += APP_VERSION=\\\"$$VERSION\\\" CUTTER_ENABLE_JUPYTER { message("Jupyter support enabled.") DEFINES += CUTTER_ENABLE_JUPYTER @@ -96,6 +97,8 @@ macx:CUTTER_BUNDLE_R2_APPBUNDLE { DEFINES += MACOS_R2_BUNDLED } +QMAKE_SUBSTITUTES += CutterConfig.h.in + SOURCES += \ Main.cpp \ Cutter.cpp \ diff --git a/src/CutterApplication.cpp b/src/CutterApplication.cpp index 6c5bb6ff..428007ab 100644 --- a/src/CutterApplication.cpp +++ b/src/CutterApplication.cpp @@ -16,11 +16,13 @@ #endif #include "plugins/CutterPlugin.h" +#include "CutterConfig.h" + CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv) { setOrganizationName("Cutter"); setApplicationName("Cutter"); - setApplicationVersion(APP_VERSION); + setApplicationVersion(CUTTER_VERSION_FULL); setWindowIcon(QIcon(":/img/cutter.svg")); setAttribute(Qt::AA_DontShowIconsInMenus); diff --git a/src/CutterConfig.h.in b/src/CutterConfig.h.in new file mode 100644 index 00000000..b8aa8a9e --- /dev/null +++ b/src/CutterConfig.h.in @@ -0,0 +1,11 @@ + +#ifndef CUTTER_CONFIG_H +#define CUTTER_CONFIG_H + +#define CUTTER_VERSION_MAJOR $$CUTTER_VERSION_MAJOR +#define CUTTER_VERSION_MINOR $$CUTTER_VERSION_MINOR +#define CUTTER_VERSION_PATCH $$CUTTER_VERSION_PATCH + +#define CUTTER_VERSION_FULL \"$${CUTTER_VERSION_MAJOR}.$${CUTTER_VERSION_MINOR}.$${CUTTER_VERSION_PATCH}\" + +#endif diff --git a/src/CutterConfig.h.meson.in b/src/CutterConfig.h.meson.in new file mode 100644 index 00000000..20d5b504 --- /dev/null +++ b/src/CutterConfig.h.meson.in @@ -0,0 +1,11 @@ + +#ifndef CUTTER_CONFIG_H +#define CUTTER_CONFIG_H + +#define CUTTER_VERSION_MAJOR @CUTTER_VERSION_MAJOR@ +#define CUTTER_VERSION_MINOR @CUTTER_VERSION_MINOR@ +#define CUTTER_VERSION_PATCH @CUTTER_VERSION_PATCH@ + +#define CUTTER_VERSION_FULL "@CUTTER_VERSION_MAJOR@.@CUTTER_VERSION_MINOR@.@CUTTER_VERSION_PATCH@" + +#endif diff --git a/src/cmake/QMakeConfigureFile.cmake b/src/cmake/QMakeConfigureFile.cmake new file mode 100644 index 00000000..83556617 --- /dev/null +++ b/src/cmake/QMakeConfigureFile.cmake @@ -0,0 +1,26 @@ +# ------------------------ +# QMakeConfigureFile.cmake +# ------------------------ + +function(_prepare_qmake_configure_file INPUT OUTPUT) + # copyonly configure once to trigger re-running cmake on changes + configure_file("${INPUT}" "${OUTPUT}.qmake.in" COPYONLY) + file(READ "${INPUT}" CONTENT) + + # replace \" with " + string(REPLACE "\\\"" "\"" CONTENT "${CONTENT}") + # replace variables + string(REGEX REPLACE "\\\$\\\$([A-Za-z0-9_]+)" "\${\\1}" CONTENT "${CONTENT}") + string(REGEX REPLACE "\\\$\\\${([A-Za-z0-9_]+)}" "\${\\1}" CONTENT "${CONTENT}") + + file(WRITE "${OUTPUT}.cmake.in" "${CONTENT}") +endfunction() + +# qmake_configure_file( ) +# +# like configure_file, but using qmake syntax +# +macro(qmake_configure_file INPUT OUTPUT) + _prepare_qmake_configure_file("${INPUT}" "${OUTPUT}") + configure_file("${OUTPUT}.cmake.in" "${OUTPUT}") +endmacro() \ No newline at end of file diff --git a/src/dialogs/AboutDialog.cpp b/src/dialogs/AboutDialog.cpp index e59361fe..10e276b4 100644 --- a/src/dialogs/AboutDialog.cpp +++ b/src/dialogs/AboutDialog.cpp @@ -5,6 +5,8 @@ #include "r_version.h" #include "utils/Configuration.h" +#include "CutterConfig.h" + AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog) @@ -14,7 +16,7 @@ AboutDialog::AboutDialog(QWidget *parent) : ui->logoSvgWidget->load(Config()->getLogoFile()); ui->label->setText(tr("Cutter" - "Version " CUTTER_VERSION "" + "Version " CUTTER_VERSION_FULL "" "Using r2-" R2_GITTAP "Optional Features:" "Jupyter: %1" diff --git a/src/meson.build b/src/meson.build index e976eca6..402aa4b3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -24,7 +24,18 @@ sources = run_command(parse_cmd + ['SOURCES'], check: true).stdout().split(';') headers = run_command(parse_cmd + ['HEADERS'], check: true).stdout().split(';') ui_files = run_command(parse_cmd + ['FORMS'], check: true).stdout().split(';') qresources = run_command(parse_cmd + ['RESOURCES'], check: true).stdout().split(';') -version = run_command(parse_cmd + ['VERSION'], check: true).stdout() +version_major = run_command(parse_cmd + ['CUTTER_VERSION_MAJOR'], check: true).stdout() +version_minor = run_command(parse_cmd + ['CUTTER_VERSION_MINOR'], check: true).stdout() +version_patch = run_command(parse_cmd + ['CUTTER_VERSION_PATCH'], check: true).stdout() + +conf_json = '''{"CUTTER_VERSION_MAJOR":@0@, + "CUTTER_VERSION_MINOR":@1@, + "CUTTER_VERSION_PATCH":@2@}'''.format( + version_major, version_minor, version_patch) +configure_file(input: 'CutterConfig.h.in', + output: 'CutterConfig.h', + command: [py3_exe, '../scripts/meson_configure_qmake_in.py', '@INPUT@', '@OUTPUT0@', conf_json]) +conf_inc = include_directories('.') sp_dir = join_paths(meson.source_root(), 'subprojects') sp_r2_dir = join_paths(sp_dir, 'radare2') @@ -76,13 +87,11 @@ if host_machine.system() == 'windows' add_project_link_arguments(join_paths(qt_host_libs, qtmain_libname), language: 'cpp') endif -add_project_arguments('-DAPP_VERSION="@0@"'.format(version), language: 'cpp') - cutter_exe = executable( 'Cutter', moc_files, gui_app: true, sources: sources, - include_directories: platform_inc, + include_directories: [platform_inc, conf_inc], dependencies: deps, ) diff --git a/src/utils/PythonAPI.cpp b/src/utils/PythonAPI.cpp index 892e9c5a..e639a099 100644 --- a/src/utils/PythonAPI.cpp +++ b/src/utils/PythonAPI.cpp @@ -6,13 +6,15 @@ #include "JupyterConnection.h" #include "NestedIPyKernel.h" +#include "CutterConfig.h" + #include PyObject *api_version(PyObject *self, PyObject *null) { Q_UNUSED(self) Q_UNUSED(null) - return PyUnicode_FromString("Cutter version " CUTTER_VERSION); + return PyUnicode_FromString("Cutter version " CUTTER_VERSION_FULL); } PyObject *api_cmd(PyObject *self, PyObject *args)
Optional Features:" "Jupyter: %1" diff --git a/src/meson.build b/src/meson.build index e976eca6..402aa4b3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -24,7 +24,18 @@ sources = run_command(parse_cmd + ['SOURCES'], check: true).stdout().split(';') headers = run_command(parse_cmd + ['HEADERS'], check: true).stdout().split(';') ui_files = run_command(parse_cmd + ['FORMS'], check: true).stdout().split(';') qresources = run_command(parse_cmd + ['RESOURCES'], check: true).stdout().split(';') -version = run_command(parse_cmd + ['VERSION'], check: true).stdout() +version_major = run_command(parse_cmd + ['CUTTER_VERSION_MAJOR'], check: true).stdout() +version_minor = run_command(parse_cmd + ['CUTTER_VERSION_MINOR'], check: true).stdout() +version_patch = run_command(parse_cmd + ['CUTTER_VERSION_PATCH'], check: true).stdout() + +conf_json = '''{"CUTTER_VERSION_MAJOR":@0@, + "CUTTER_VERSION_MINOR":@1@, + "CUTTER_VERSION_PATCH":@2@}'''.format( + version_major, version_minor, version_patch) +configure_file(input: 'CutterConfig.h.in', + output: 'CutterConfig.h', + command: [py3_exe, '../scripts/meson_configure_qmake_in.py', '@INPUT@', '@OUTPUT0@', conf_json]) +conf_inc = include_directories('.') sp_dir = join_paths(meson.source_root(), 'subprojects') sp_r2_dir = join_paths(sp_dir, 'radare2') @@ -76,13 +87,11 @@ if host_machine.system() == 'windows' add_project_link_arguments(join_paths(qt_host_libs, qtmain_libname), language: 'cpp') endif -add_project_arguments('-DAPP_VERSION="@0@"'.format(version), language: 'cpp') - cutter_exe = executable( 'Cutter', moc_files, gui_app: true, sources: sources, - include_directories: platform_inc, + include_directories: [platform_inc, conf_inc], dependencies: deps, ) diff --git a/src/utils/PythonAPI.cpp b/src/utils/PythonAPI.cpp index 892e9c5a..e639a099 100644 --- a/src/utils/PythonAPI.cpp +++ b/src/utils/PythonAPI.cpp @@ -6,13 +6,15 @@ #include "JupyterConnection.h" #include "NestedIPyKernel.h" +#include "CutterConfig.h" + #include PyObject *api_version(PyObject *self, PyObject *null) { Q_UNUSED(self) Q_UNUSED(null) - return PyUnicode_FromString("Cutter version " CUTTER_VERSION); + return PyUnicode_FromString("Cutter version " CUTTER_VERSION_FULL); } PyObject *api_cmd(PyObject *self, PyObject *args)