diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/github_workflow_README.md b/share/qtcreator/templates/wizards/qtcreatorplugin/github_workflow_README.md new file mode 100644 index 00000000000..d4019e46440 --- /dev/null +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/github_workflow_README.md @@ -0,0 +1,41 @@ +# GitHub Actions & Workflows + +The `build_qmake.yml` in this directory adds a [GitHub action][1] and workflow that builds +your plugin anytime you push commits to GitHub on Windows, Linux and macOS. + +The build artifacts can be downloaded from GitHub and be installed into an existing Qt Creator +installation. + +When you push a tag, the workflow also creates a new release on GitHub. + +## Keeping it up to date + +Near the top of the file you find a section starting with `env:`. + +The value for `QT_VERSION` specifies the Qt version to use for building the plugin. + +The value for `QT_CREATOR_VERSION` specifies the Qt Creator version to use for building the plugin. + +You need to keep these two values updated for different versions of your plugin, and take care +that the Qt version and Qt Creator version you specify are compatible. + +## What it does + +The build job consists of several steps: + +* Install required packages on the build host +* Download, unpack and install the binary for the Qt version +* Download and unpack the binary for the Qt Creator version +* Build the plugin and upload the plugin libraries to GitHub +* If a tag is pushed, create a release on GitHub for the tag, including zipped plugin libraries + for download + +## Limitations + +If your plugin requires additional resources besides the plugin library, you need to adapt the +script accordingly. + +Only released versions of Qt and Qt Creator are supported. Building against Qt Creator snapshots +is currently not supported. + +[1]: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/github_workflow_build_qmake.yml b/share/qtcreator/templates/wizards/qtcreatorplugin/github_workflow_build_qmake.yml new file mode 100644 index 00000000000..8c86a99020d --- /dev/null +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/github_workflow_build_qmake.yml @@ -0,0 +1,309 @@ +name: Build plugin + +on: [push] + +env: + QT_VERSION: %{JS: Util.qtVersion()} + QT_CREATOR_VERSION: %{JS: Util.qtCreatorVersion()} + PLUGIN_PRO: %{ProFile} + PLUGIN_NAME: %{PluginName} + +jobs: + build: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + matrix: + config: + - { + name: "Windows Latest x64", artifact: "Windows-x64.zip", + os: windows-latest, + environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat" + } + - { + name: "Windows Latest x86", artifact: "Windows-x86.zip", + os: windows-latest, + environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars32.bat" + } + - { + name: "Linux Latest x64", artifact: "Linux-x64.zip", + os: ubuntu-latest + } + - { + name: "macOS Latest x64", artifact: "macOS-x64.zip", + os: macos-latest + } + + steps: + - uses: actions/checkout@v1 + + - name: Installing system libs + shell: cmake -P {0} + run: | + if ("${{ runner.os }}" STREQUAL "Linux") + execute_process( + COMMAND sudo apt install libgl1-mesa-dev + ) + endif() + + - name: Download Qt + id: qt + shell: cmake -P {0} + run: | + set(qt_version $ENV{QT_VERSION}) + + string(REPLACE "." "" qt_version_dotless "${qt_version}") + if ("${{ runner.os }}" STREQUAL "Windows") + set(url_os "windows_x86") + if ("${{ matrix.config.environment_script }}" MATCHES "vcvars64.bat") + set(qt_package_name "qt.qt5.${qt_version_dotless}.win64_msvc2017_64") + set(qt_dir_prefix "${qt_version}/msvc2017_64") + elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars32.bat") + set(qt_package_name "qt.qt5.${qt_version_dotless}.win32_msvc2017") + set(qt_dir_prefix "${qt_version}/msvc2017") + else() + endif() + elseif ("${{ runner.os }}" STREQUAL "Linux") + set(url_os "linux_x64") + set(qt_package_name "qt.qt5.${qt_version_dotless}.gcc_64") + set(qt_dir_prefix "${qt_version}/gcc_64") + elseif ("${{ runner.os }}" STREQUAL "macOS") + set(url_os "mac_x64") + set(qt_package_name "qt.qt5.${qt_version_dotless}.clang_64") + set(qt_dir_prefix "${qt_version}/clang_64") + endif() + + set(qt_base_url "https://download.qt.io/online/qtsdkrepository/${url_os}/desktop/qt5_${qt_version_dotless}") + file(DOWNLOAD "${qt_base_url}/Updates.xml" ./Updates.xml SHOW_PROGRESS) + + file(READ ./Updates.xml updates_xml) + string(REGEX MATCH "${qt_package_name}.*([0-9+-.]+).*qtbase([a-zA-Z0-9_-]+).7z" + updates_xml_output "${updates_xml}") + set(package_version ${CMAKE_MATCH_1}) + set(package_suffix ${CMAKE_MATCH_2}) + string(REPLACE "-debug-symbols" "" package_suffix "${package_suffix}") + + # Workaround for CMake's greedy regex + if ("${{ matrix.config.environment_script }}" MATCHES "vcvars32.bat") + string(REPLACE "X86_64" "X86" package_suffix "${package_suffix}") + endif() + + file(MAKE_DIRECTORY qt5) + + # Save the path for other steps + file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qt5/${qt_dir_prefix}" qt_dir) + message("::set-output name=qt_dir::${qt_dir}") + + foreach(package qtbase qtdeclarative qttools qtsvg) + file(DOWNLOAD + "${qt_base_url}/${qt_package_name}/${package_version}${package}${package_suffix}.7z" ./${package}.7z + SHOW_PROGRESS + ) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../${package}.7z WORKING_DIRECTORY qt5) + endforeach() + + file(READ "qt5/${qt_dir_prefix}/mkspecs/qconfig.pri" qtconfig) + string(REPLACE "Enterprise" "OpenSource" qtconfig "${qtconfig}") + string(REPLACE "licheck.exe" "" qtconfig "${qtconfig}") + string(REPLACE "licheck64" "" qtconfig "${qtconfig}") + string(REPLACE "licheck_mac" "" qtconfig "${qtconfig}") + file(WRITE "qt5/${qt_dir_prefix}/mkspecs/qconfig.pri" "${qtconfig}") + + - name: Download Qt Creator + id: qt_creator + shell: cmake -P {0} + run: | + string(REGEX MATCH "([0-9]+.[0-9]+).[0-9]+" outvar "$ENV{QT_CREATOR_VERSION}") + set(qtc_base_url "https://download.qt.io/official_releases/qtcreator/${CMAKE_MATCH_1}/$ENV{QT_CREATOR_VERSION}") + + if ("${{ runner.os }}" STREQUAL "Windows") + set(qtc_output_directory "qtcreator/lib/qtcreator/plugins") + set(qtc_binary_name "$ENV{PLUGIN_NAME}4.dll") + if ("${{ matrix.config.environment_script }}" MATCHES "vcvars64.bat") + set(qtc_platform "windows_msvc2017_x64") + elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars32.bat") + set(qtc_platform "windows_msvc2017_x86") + endif() + elseif ("${{ runner.os }}" STREQUAL "Linux") + set(qtc_output_directory "qtcreator/lib/qtcreator/plugins") + set(qtc_binary_name "lib$ENV{PLUGIN_NAME}.so") + set(qtc_platform "linux_gcc_64_rhel72") + elseif ("${{ runner.os }}" STREQUAL "macOS") + set(qtc_output_directory "qtcreator/bin/Qt Creator.app/Contents/PlugIns") + set(qtc_binary_name "lib$ENV{PLUGIN_NAME}.dylib") + set(qtc_platform "mac_x64") + endif() + + # Save the path for other steps + message("::set-output name=qtc_binary_name::${qtc_binary_name}") + message("::set-output name=qtc_output_directory::${qtc_output_directory}") + + file(MAKE_DIRECTORY qtcreator) + + foreach(package qtcreator qtcreator_dev) + file(DOWNLOAD + "${qtc_base_url}/installer_source/${qtc_platform}/${package}.7z" ./${package}.7z SHOW_PROGRESS) + execute_process(COMMAND + ${CMAKE_COMMAND} -E tar xvf ../${package}.7z WORKING_DIRECTORY qtcreator) + endforeach() + + if ("${{ runner.os }}" STREQUAL "macOS") + execute_process( + COMMAND ${CMAKE_COMMAND} -E make_directory qtcreator/bin + COMMAND ${CMAKE_COMMAND} -E create_symlink + "$ENV{GITHUB_WORKSPACE}/qtcreator/Qt Creator.app" + "$ENV{GITHUB_WORKSPACE}/qtcreator/bin/Qt Creator.app" + ) + endif() + + - name: Configure + shell: cmake -P {0} + run: | + if ("${{ runner.os }}" STREQUAL "Windows" AND NOT "x${{ matrix.config.environment_script }}" STREQUAL "x") + execute_process( + COMMAND "${{ matrix.config.environment_script }}" && set + OUTPUT_FILE environment_script_output.txt + ) + file(STRINGS environment_script_output.txt output_lines) + foreach(line IN LISTS output_lines) + if (line MATCHES "^([a-zA-Z0-9_-]+)=(.*)$") + set(ENV{${CMAKE_MATCH_1}} "${CMAKE_MATCH_2}") + + # Set for other steps + message("::set-env name=${CMAKE_MATCH_1}::${CMAKE_MATCH_2}") + endif() + endforeach() + endif() + + file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/qtcreator" qtcreator_dir) + + execute_process( + COMMAND ${{ steps.qt.outputs.qt_dir }}/bin/qmake + $ENV{PLUGIN_PRO} + CONFIG+=release + IDE_SOURCE_TREE="${qtcreator_dir}" + IDE_BUILD_TREE="${qtcreator_dir}" + RESULT_VARIABLE result + ) + if (NOT result EQUAL 0) + message(FATAL_ERROR "Bad exit status") + endif() + + - name: Build + shell: cmake -P {0} + run: | + if ("${{ runner.os }}" STREQUAL "Windows") + set(ENV{PATH} "${{ steps.qt.outputs.qt_dir }}/bin/;$ENV{PATH}") + else() + set(ENV{PATH} "${{ steps.qt.outputs.qt_dir }}/bin/:$ENV{PATH}") + set(ENV{LD_LIBRARY_PATH} "qtcreator/lib/Qt/lib:$ENV{LD_LIBRARY_PATH}") + endif() + + include(ProcessorCount) + ProcessorCount(N) + + set(make_program make -j ${N}) + if ("${{ runner.os }}" STREQUAL "Windows") + set(make_program "qtcreator/bin/jom") + endif() + + execute_process( + COMMAND ${make_program} + RESULT_VARIABLE result + ) + if (NOT result EQUAL 0) + message(FATAL_ERROR "Bad exit status") + endif() + + file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/$ENV{PLUGIN_NAME}-$ENV{QT_CREATOR_VERSION}-${{ matrix.config.artifact }}" artifact) + + execute_process(COMMAND + ${CMAKE_COMMAND} -E tar cvf ${artifact} --format=zip "${{ steps.qt_creator.outputs.qtc_binary_name }}" + WORKING_DIRECTORY "${{ steps.qt_creator.outputs.qtc_output_directory }}" + ) + + - uses: actions/upload-artifact@v1 + id: upload_artifact + with: + path: ./${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }} + name: ${{ env.PLUGIN_NAME}}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }} + + release: + if: contains(github.ref, 'tags/v') + runs-on: ubuntu-latest + needs: build + + steps: + - name: Create Release + id: create_release + uses: actions/create-release@v1.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + + - name: Store Release url + run: | + echo "${{ steps.create_release.outputs.upload_url }}" > ./upload_url + + - uses: actions/upload-artifact@v1 + with: + path: ./upload_url + name: upload_url + + publish: + if: contains(github.ref, 'tags/v') + + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + matrix: + config: + - { + name: "Windows Latest x64", artifact: "Windows-x64.zip", + os: ubuntu-latest + } + - { + name: "Windows Latest x86", artifact: "Windows-x86.zip", + os: ubuntu-latest + } + - { + name: "Linux Latest x64", artifact: "Linux-x64.zip", + os: ubuntu-latest + } + - { + name: "macOS Latest x64", artifact: "macOS-x64.zip", + os: macos-latest + } + needs: release + + steps: + - name: Download artifact + uses: actions/download-artifact@v1 + with: + name: ${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }} + path: ./ + + - name: Download URL + uses: actions/download-artifact@v1 + with: + name: upload_url + path: ./ + - id: set_upload_url + run: | + upload_url=`cat ./upload_url` + echo ::set-output name=upload_url::$upload_url + + - name: Upload to Release + id: upload_to_release + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.set_upload_url.outputs.upload_url }} + asset_path: ./${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }} + asset_name: ${{ env.PLUGIN_NAME }}-${{ env.QT_CREATOR_VERSION }}-${{ matrix.config.artifact }} + asset_content_type: application/zip diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.pro b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.pro index 6425af4270b..82328264a7a 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.pro +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.pro @@ -10,6 +10,10 @@ HEADERS += \\ %{GlobalHdrFileName} \\ %{ConstantsHdrFileName} +DISTFILES += \\ + .github/workflow/build_qmake.yml \\ + .github/workflow/README.md + # Qt Creator linking ## Either set the IDE_SOURCE_TREE when running qmake, diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json b/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json index d4ff57617d5..cbaca648fe6 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json @@ -14,7 +14,7 @@ [ { "key": "ProjectFile", "value": "%{ProFile}" }, { "key": "PluginNameLower", "value": "%{JS: value('PluginName').toLowerCase()}"}, - { "key": "ProFile", "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('PluginNameLower'), 'pro')}" }, + { "key": "ProFile", "value": "%{JS: Util.fileName(value('PluginNameLower'), 'pro')}" }, { "key": "PluginJsonFile", "value": "%{JS: Util.fileName(value('PluginName'), 'json.in')}" }, { "key": "LibraryDefine", "value": "%{JS: Cpp.headerGuard(value('PluginName')) + '_LIBRARY'}" }, { "key": "LibraryExport", "value": "%{JS: Cpp.headerGuard(value('PluginName')) + '_EXPORT'}" }, @@ -189,6 +189,14 @@ "target": "%{ProFile}", "openAsProject": true }, + { + "source": "github_workflow_build_qmake.yml", + "target": ".github/workflow/build_qmake.yml" + }, + { + "source": "github_workflow_README.md", + "target": ".github/workflow/README.md" + }, { "source": "myplugin.cpp", "target": "%{SrcFileName}", diff --git a/src/plugins/coreplugin/corejsextensions.cpp b/src/plugins/coreplugin/corejsextensions.cpp index b5cbd089732..6b6a380108f 100644 --- a/src/plugins/coreplugin/corejsextensions.cpp +++ b/src/plugins/coreplugin/corejsextensions.cpp @@ -25,6 +25,8 @@ #include "corejsextensions.h" +#include + #include #include #include @@ -36,6 +38,16 @@ namespace Core { namespace Internal { +QString UtilsJsExtension::qtVersion() const +{ + return QLatin1String(qVersion()); +} + +QString UtilsJsExtension::qtCreatorVersion() const +{ + return QLatin1String(Constants::IDE_VERSION_DISPLAY); +} + QString UtilsJsExtension::toNativeSeparators(const QString &in) const { return QDir::toNativeSeparators(in); diff --git a/src/plugins/coreplugin/corejsextensions.h b/src/plugins/coreplugin/corejsextensions.h index 2390d91a33f..bbdc8762512 100644 --- a/src/plugins/coreplugin/corejsextensions.h +++ b/src/plugins/coreplugin/corejsextensions.h @@ -41,6 +41,10 @@ class UtilsJsExtension : public QObject public: UtilsJsExtension(QObject *parent = nullptr) : QObject(parent) { } + // General information + Q_INVOKABLE QString qtVersion() const; + Q_INVOKABLE QString qtCreatorVersion() const; + // File name conversions: Q_INVOKABLE QString toNativeSeparators(const QString &in) const; Q_INVOKABLE QString fromNativeSeparators(const QString &in) const;