google / flutter-desktop-embedding

Experimental plugins for Flutter for Desktop
Apache License 2.0
7.1k stars 607 forks source link

Linux builds should support debian installers #818

Closed cosmotek closed 3 years ago

cosmotek commented 3 years ago

Hi! I was hoping someone could point me to docs or a part of the repo where one might modify how flutter handles building for linux. I'd like to contribute by adding support for Debian packages and AppImages. The build process for these packages is relatively simple, so if someone can help me figure out where to look, I'm sure I can get something working.

cosmotek commented 3 years ago

I've done some quick digging and not found anything useful... yet. My plan is to take the binary and libs produced by the current linux build process, add in an app icon (ideally specified in the pubspec), as well as any version info or useful metadata, and embed this into a .deb or alternatively an AppImage.

cosmotek commented 3 years ago

I'm willing to bet I will also need to update the doctor command to include tooling for this build process. Likely dkpg-deb and appimagetool will need to be included.

Might be able to get away with rolling .deb packages without any tooling.... which would be ideal. We'll see.

cosmotek commented 3 years ago

Oooo. Think I found it.

https://github.com/flutter/flutter/blob/master/packages/flutter_tools/lib/src/commands/build_linux.dart

cosmotek commented 3 years ago

FYI, keeping this open for a little to track progress and notes in the case where someone else wants to get involved.

So far, I have gotten deb builds working using CPack. The process is pretty simple, but I'm still trying to figure out how to handle some of this CMake config (I am no expert at CMake). I'm getting an actual deb, but the deps aren't accurate, the directories embedded in the deb for the binary and libs are a bit off (they include the project path at the moment), and some of the info in the control file is coming from the wrong places.

Once I get more of this tacked down, I assume I'll need to update the flutter create code to add my CMakeLists.txt file, as well as including a .desktop file.

Does anyone have ideas on how we should handle specifying out the desktop icon? Right now I am just dumping it into the linux directory, and including it directly via the CMakeLists.txt file. Ideally this should be set from the pubspec IMO.

cosmotek commented 3 years ago

At this point I have almost everything worked out, but I am trying to figure out how we're linking libflutter to the binary produced for linux. Running ldd against a flutter-produced binary gives me the following:

$ ldd spot
    linux-vdso.so.1 (0x00007ffe8b9ec000)
    liburl_launcher_linux_plugin.so => /home/cosmotek/code/flutter/spot/build/linux/release/_CPack_Packages/Linux/DEB/spot-0.0.0/usr/opt/spot/./lib/liburl_launcher_linux_plugin.so (0x00007facddea8000)
    libgtk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 (0x00007facdd5a0000)
    libgdk-3.so.0 => /usr/lib/x86_64-linux-gnu/libgdk-3.so.0 (0x00007facdd2aa000)
    libpangocairo-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 (0x00007facdd09d000)
    libpango-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 (0x00007facdce50000)
    libatk-1.0.so.0 => /usr/lib/x86_64-linux-gnu/libatk-1.0.so.0 (0x00007facdcc2a000)
    libcairo-gobject.so.2 => /usr/lib/x86_64-linux-gnu/libcairo-gobject.so.2 (0x00007facdca21000)
    libcairo.so.2 => /usr/lib/x86_64-linux-gnu/libcairo.so.2 (0x00007facdc704000)
    libgdk_pixbuf-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0 (0x00007facdc4e0000)
    libflutter_linux_gtk.so => /home/cosmotek/code/flutter/spot/build/linux/release/_CPack_Packages/Linux/DEB/spot-0.0.0/usr/opt/spot/./lib/libflutter_linux_gtk.so (0x00007facdb90d000)

liburl_launcher_linux_plugin specifically points out the issue. The reference shown is specific to the build path. I'm not sure if this is part of the ninja build scripts or the cmake process. Hopefully I can change this in cmake.

cosmotek commented 3 years ago

Major progress... got working debs building and installing using the following CMakeLists.txt file. I'm sure it can be improved (pros, please make recommendations), but for now it mostly works. There's a small error that pops up related to not having the permissions to create files in /usr/share/ but somehow the deb build still works (even with a clean build). Next steps are as follows:

Just some notes:

cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)

if(CMAKE_BUILD_TYPE MATCHES "Debug")
  set(BINARY_NAME "spot")
  set(APPLICATION_ID "com.example.spot")

  cmake_policy(SET CMP0063 NEW)

  set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")

  # Configure build options.
  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE "Debug" CACHE
      STRING "Flutter build mode" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
      "Debug" "Profile" "Release")
  endif()

  # Compilation settings that should be applied to most targets.
  function(APPLY_STANDARD_SETTINGS TARGET)
    target_compile_features(${TARGET} PUBLIC cxx_std_14)
    target_compile_options(${TARGET} PRIVATE -Wall -Werror)
    target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
    target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
  endfunction()

  set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")

  # Flutter library and tool build rules.
  add_subdirectory(${FLUTTER_MANAGED_DIR})

  # System-level dependencies.
  find_package(PkgConfig REQUIRED)
  pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)

  add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

  # Application build
  add_executable(${BINARY_NAME}
    "main.cc"
    "my_application.cc"
    "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
  )
  apply_standard_settings(${BINARY_NAME})
  target_link_libraries(${BINARY_NAME} PRIVATE flutter)
  target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
  add_dependencies(${BINARY_NAME} flutter_assemble)
  # Only the install-generated bundle's copy of the executable will launch
  # correctly, since the resources must in the right relative locations. To avoid
  # people trying to run the unbundled copy, put it in a subdirectory instead of
  # the default top-level location.
  set_target_properties(${BINARY_NAME}
    PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
  )

  # Generated plugin build rules, which manage building the plugins and adding
  # them to the application.
  include(flutter/generated_plugins.cmake)

  # === Installation ===
  # By default, "installing" just makes a relocatable bundle in the build
  # directory.
  set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
  if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
    set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
  endif()

  # Start with a clean build bundle directory every time.
  install(CODE "
    file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
    " COMPONENT Runtime)

  set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
  set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")

  install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
    COMPONENT Runtime)

  install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
    COMPONENT Runtime)

  install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
    COMPONENT Runtime)

  if(PLUGIN_BUNDLED_LIBRARIES)
    install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
      DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
      COMPONENT Runtime)
  endif()

  # Fully re-copy the assets directory on each build to avoid having stale files
  # from a previous install.
  set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
  install(CODE "
    file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
    " COMPONENT Runtime)
  install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
    DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
endif()

if(CMAKE_BUILD_TYPE MATCHES "Release")
  set(BINARY_NAME "spot")
  set(CPACK_PACKAGE_NAME "spot")
  set(APPLICATION_ID "com.example.spot")

  # TODO determine how to pull these from the pubspec yaml
  set(MAJOR_VERSION "0")
  set(MINOR_VERSION "0")
  set(PATCH_VERSION "0")

  IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
  INCLUDE(InstallRequiredSystemLibraries)

  # not sure if this is necessary...
  SET(CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}")
  SET(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}")
  SET(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}")

  SET(CPACK_SET_DESTDIR "OFF")
  SET(CPACK_GENERATOR "DEB")
  set(CPACK_PACKAGING_INSTALL_PREFIX "/opt")
  # set(CPACK_NATIVE_INSTALL_DIRECTORY "")
  set(CMAKE_INSTALL_PREFIX "/opt")
  # set(CPACK_INSTALL_DIRECTORY "")

  SET(CPACK_PACKAGE_DESCRIPTION "A new Flutter project.")
  SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A new Flutter project.")
  SET(CPACK_PACKAGE_VENDOR "Seth Moeckel")
  SET(CPACK_PACKAGE_CONTACT "seth.moeckel@gmail.com")

  SET(CPACK_PACKAGE_FILE_NAME "${BINARY_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
  SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${BINARY_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}")
  SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
  # SET(CPACK_DEBIAN_PACKAGE_SECTION "kde")
  SET(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
  #dependencies for this service menu
  # SET(CPACK_DEBIAN_PACKAGE_DEPENDS " kate , dolphin ")

  SET(CPACK_COMPONENTS_ALL Libraries ApplicationData)
  INCLUDE(CPack)

  # Install the .desktop file and icon
  INSTALL(FILES spot.desktop DESTINATION /usr/share/applications/)
  INSTALL(FILES spot.png DESTINATION /usr/share/icons/hicolor/48x48/apps/)

  # set(CPACK_PACKAGING_INSTALL_PREFIX "/opt")
  # # set(CPACK_NATIVE_INSTALL_DIRECTORY "")
  # set(CMAKE_INSTALL_PREFIX "/opt")

  # Set postinstall script
  # set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${PROJECT_NAME}/contrib/postinst")
  # set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "postinst")

  ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")

  cmake_policy(SET CMP0063 NEW)

  # set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
  # set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
  # set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
  set(CMAKE_INSTALL_RPATH "/opt/spot/lib")

  # Configure build options.
  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE "Debug" CACHE
      STRING "Flutter build mode" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
      "Debug" "Profile" "Release")
  endif()

  # Compilation settings that should be applied to most targets.
  function(APPLY_STANDARD_SETTINGS TARGET)
    target_compile_features(${TARGET} PUBLIC cxx_std_14)
    target_compile_options(${TARGET} PRIVATE -Wall -Werror)
    target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
    target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
  endfunction()

  set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")

  # Flutter library and tool build rules.
  add_subdirectory(${FLUTTER_MANAGED_DIR})

  # System-level dependencies.
  find_package(PkgConfig REQUIRED)
  pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)

  add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

  # Application build
  add_executable(${BINARY_NAME}
    "main.cc"
    "my_application.cc"
    "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
  )
  apply_standard_settings(${BINARY_NAME})
  target_link_libraries(${BINARY_NAME} PRIVATE flutter)
  target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
  add_dependencies(${BINARY_NAME} flutter_assemble)
  # Only the install-generated bundle's copy of the executable will launch
  # correctly, since the resources must in the right relative locations. To avoid
  # people trying to run the unbundled copy, put it in a subdirectory instead of
  # the default top-level location.
  set_target_properties(${BINARY_NAME}
    PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
  )

  # Generated plugin build rules, which manage building the plugins and adding
  # them to the application.
  include(flutter/generated_plugins.cmake)

  # === Installation ===
  # By default, "installing" just makes a relocatable bundle in the build
  # directory.
  set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
  if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
    set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
  endif()

  # Start with a clean build bundle directory every time.
  install(CODE "
    file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
    " COMPONENT Runtime)

  set(INSTALL_BUNDLE_DATA_DIR "${BINARY_NAME}/data/")
  set(INSTALL_BUNDLE_LIB_DIR "${BINARY_NAME}/lib/")

  install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${BINARY_NAME}/"
    COMPONENT Runtime)

  install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
    COMPONENT Runtime)

  install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
    COMPONENT Runtime)

  if(PLUGIN_BUNDLED_LIBRARIES)
    install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
      DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
      COMPONENT Runtime)
  endif()

  # Fully re-copy the assets directory on each build to avoid having stale files
  # from a previous install.
  set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
  install(CODE "
    file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
    " COMPONENT Runtime)
  install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
    DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)

  install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
    COMPONENT Runtime)
endif()

Here's the desktop entry I used:

[Desktop Entry]
Type=Application
Encoding=UTF-8
Name=Spot
Comment=A Flutter App
Exec=/opt/spot/spot
Icon=spot.png
Terminal=false
cosmotek commented 3 years ago

Obviously this file needs some clean-up and modifications to make it less specific to my test case. Its late... I'll tackle this tomorrow.

stuartmorgan commented 3 years ago

Please read https://github.com/google/flutter-desktop-embedding#feedback. Closing as this has nothing to do with any of the plugins here, and is thus not on topic for this repository.

As for where this should go: Making a tool to package Flutter applications into Debian installers is definitely interesting, and something I'm glad to see someone tackling, but we currently have no plans to build all of the possible installer formats into the flutter tool (just as the snap support for Flutter Linux apps is not part of flutter). I would recommend setting up a new GitHub project for a script/tool to run to package Flutter build output into your installer.

cosmotek commented 3 years ago

I understand this being the wrong location, but I totally disagree that this tooling should be separate. Deb installers are extremely popular and very easy to convert to different formats for distros that don't support it... but they aren't trivial to create. I really don't understand why Android, iOS and MacOS produce distributable formats but Linux shouldn't? What's the use-case for using flutter build linux --release and getting back a set of files that you need to package... this is poor UX imo. I think a Debian image alongside the current binary output is the best solution. That being said I don't think we should implement outputs to any other formats.

I'm sure this is the wrong location for this discussion, where should this happen?

stuartmorgan commented 3 years ago

The Flutter issue tracker is where discussions about Flutter functionality should happen, per the README I linked above.