Creating a simple derivation for "Touchegg", but the binary returns an error!

Hello!
I was looking at an extension to get gestures for Gnome on x11, and it a program called touchegg. I saw that this program was actually packaged for Nix, but I could not get it to work. This led me to believe that a newer version of touchegg was needed, and since it had no maintainer, I decided to try and create a derivation for it. (If anyone has gotten this to work I would also be interested in hearing how!)

I took some inspiration from the current touchegg derivation and looked at the nix pills and some other random places, and this is what I currently have:

with (import <nixpkgs> {});

stdenv.mkDerivation rec {
  name = "touchegg";
  pname = "${name}";
  version = "2.0.8";
  src = fetchurl {
    url = "https://github.com/JoseExposito/${pname}/archive/${version}.tar.gz";
    sha256 = "6905963bec8fc6d73c39780f5ae4648dc89fda052449f267b4b5bfbadb218ce0";
  };

  nativeBuildInputs = [ 
    gcc 
    pkg-config
    gdb
    cmake
    libudev
    libinput
    pugixml
    cairo
    xorg.libX11
    xorg.libXtst
    xorg.libXrandr
    xorg.libXi
    xorg.libXdmcp
    xorg.libpthreadstubs
    xorg.libxcb
    gtk3-x11
    pcre
    mount
  ];

  configurePhase = ''
    cmake -DCMAKE_BUILD_TYPE=Release .
  '';

  buildPhase = ''
    make -j$(nproc)
  '';

  installPhase = ''
    mkdir -p $out/bin
    mv touchegg $out/bin/touchegg
    mkdir -p $out/usr/share/touchegg
    mv installation/touchegg.conf $out/usr/share/touchegg/touchegg.conf
  '';
}

When I try to run the program for the result link, I get the following error

File /usr/share/touchegg/touchegg.conf not found.

My hope was that since i copied the touchegg.conf over in the last two lines of the install phase it would be able to find it, but as is evident, it did not. I searched around for “wrapping” and so on, but I think I fundamentally do not know what I am trying to do.

So, my question is: what is the right way to fix this error? How should the environment be emulated?

Any general pointers to improving this derivation would also be helpful! (And in particular if anything is needed to make this acceptable for upstream nixpkgs)

Thanks!

First you will want to pass -DCMAKE_INSTALL_PREFIX=$out so that CMake knows it should install the project to $out instead of /usr. Also cmake package in Nixpkgs has a setup hook that will run cmake with correct flaks by default, if configurePhase is not overridden. (Same for buildPhase and installPhase).

Unfortunately, the project does not respect the GNUInstallDirs variables so no matter what you do, it will try to install stuff to /usr and load it from there. To make it work, you will need to patch its source code.

  • Delete this line
  • Change /lib in this line to ${CMAKE_INSTALL_LIBDIR}
  • here /etc${CMAKE_INSTALL_SYSCONFDIR}
  • Populate this path in CMakeLists.txt, e.g. using configure_file – this is also the reason your program cannot find the config file. It looks into /usr but Nix never installs to /usr, only to Nix store.

See also GitHub - jtojnar/cmake-snips: Portability problems I frequently encounter in projects using CMake for more information about how to make CMake projects portable.

You can do all of the changes by just replacing /usr for $out and /etc or /lib for $out/etc or $out/lib using sed/substituteInPlace calls in postPatch/preConfigure in your Nix packages but it is preferable to make the project properly portable by creating a patch against the touchegg repository and opening a pull request upstream.

3 Likes

Thank you very much!
I started a fork and am trying to implement your suggested changes. The last one is giving me some troubles, I am not too experienced with C++ and CMake. As I understand it

  • remove src/utils/paths.cpp
  • Create a file like paths.cpp.in in the cmake directory
  • add the following (or something like that) to that file
    • #cmakedefine CONFIG_PATH_CUSTOM
    • #cmakedefine CONFIG_PATH "@CONFIG_PATH@"
  • Replace the line you linked with
return std::filesystem::path{CONFIG_PATH};
  • add the following to CMakeList.txt
option(CONFIG_PATH_CUSTOM "Enable custom config path" ON)
if(CONFIG_PATH_CUSTOM)
  set(CONFIG_PATH NEW_CONFIG_PATH)
else()
  set(CONFIG_PATH "/usr/share/touchegg/touchegg.conf")
endif()
configure_file(${PROJECT_SOURCE_DIR}/cmake/paths.cpp.in ${PROJECT_SOURCE_DIR}/src/utils/paths.cpp @ONLY)
include_directories(${PROJECT_SOURCE_DIR}/src/utils/)

Does that look right? How do I set the CONFIG_PATH_CUSTOM and CONFIG_PATH then?

Thanks!

EDIT: My fork is here https://github.com/Hvassaa/touchegg

Okay I once again tried with all the changes, but I am now getting some C++ errors. Could this be related to a misconfiguration on my part? Thanks again

Sorry, this method would probably need more changes to the build file (the CMake script collects cpp files to ${SOURCE_FILES} so we would need to add it there manually). Also writing to the source directory is not very kosher.

Since this is a code, it might be actually easier to use preprocessor definitions:

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3128219..ab3b3e7 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -70,6 +70,11 @@ endif()
 # https://cmake.org/cmake/help/v3.12/module/CPack.html
 # https://cmake.org/cmake/help/v3.7/module/CPackRPM.html
 include(GNUInstallDirs)
+
+set(SHARED_CONFIG_PATH "${CMAKE_INSTALL_FULL_DATAROOTDIR}/touchegg/touchegg.conf")
+target_compile_definitions(touchegg PUBLIC SHARED_CONFIG_PATH=\"${SHARED_CONFIG_PATH}\")
+
+
 install(FILES ${PROJECT_SOURCE_DIR}/installation/touchegg.conf DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/touchegg) # /usr/share/touchegg/touchegg.conf
 install(FILES ${PROJECT_SOURCE_DIR}/installation/touchegg.service DESTINATION ${CMAKE_INSTALL_LIBDIR}/systemd/system)
 install(FILES ${PROJECT_SOURCE_DIR}/installation/touchegg.desktop DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/xdg/autostart)
@@ -106,11 +111,4 @@ list(APPEND CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/etc")
 list(APPEND CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/etc/xdg")
 list(APPEND CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/etc/xdg/autostart")
 
-option(CONFIG_PATH_CUSTOM "Enable custom config path" ON)
-if(CONFIG_PATH_CUSTOM)
-  set(CONFIG_PATH NEW_CONFIG_PATH)
-endif()
-configure_file(${PROJECT_SOURCE_DIR}/cmake/paths.cpp.in ${PROJECT_SOURCE_DIR}/src/utils/paths.cpp @ONLY)
-include_directories(${PROJECT_SOURCE_DIR}/src/utils/)
-
 include(CPack)
diff --git a/default.nix b/default.nix
index 4ae4697..c0ccbaa 100644
--- a/default.nix
+++ b/default.nix
@@ -15,7 +15,7 @@ stdenv.mkDerivation rec {
     pkg-config
     gdb
     cmake
-    libudev
+    systemd
     libinput
     pugixml
     cairo
@@ -31,15 +31,6 @@ stdenv.mkDerivation rec {
     mount
   ];
 
-  preConfigure = ''
-    CONFIG_PATH_CUSTOM=1
-    CONFIG_PATH="$out/usr/share/touchegg/touchegg.conf"
-  '';
-
-  configurePhase = ''
-    cmake -DCMAKE_INSTALL_PREFIX=$out -DCMAKE_BUILD_TYPE=Release .
-  '';
-
   # buildPhase = ''
   #   make -j$(nproc)
   # '';
diff --git a/src/utils/paths.cpp b/src/utils/paths.cpp
index 13bab15..470ef7f 100644
--- a/src/utils/paths.cpp
+++ b/src/utils/paths.cpp
@@ -25,9 +25,6 @@
 
 #include <stdexcept>
 
-#define CONFIG_PATH_CUSTOM
-#define CONFIG_PATH "NEW_CONFIG_PATH"
-
 std::filesystem::path Paths::getHomePath() {
   // $HOME should be checked first
   const char *homeEnvVar = getenv("HOME");
@@ -71,7 +68,7 @@ std::filesystem::path Paths::getUserLockFilePath() {
 }
 
 std::filesystem::path Paths::getSystemConfigFilePath() {
-  return std::filesystem::path{CONFIG_PATH};
+  return std::filesystem::path{SHARED_CONFIG_PATH};
 }
 
 void Paths::createUserConfigDir() {
1 Like

Thanks, I really appreciate the help. This CMake stuff is magic to me!

With your changes, it is now building.

When the binary is run as result/bin/touchegg it comlpains about not being able to connect to a daemon.
When I run result/bin/touchegg --daemon it says it should be run by systemd or $USER should be in the input group. So, after I added myself to the input group, it actually worked, with the extension and everything! Brilliant!

I guess it would be desirable for it to work with systemd. There is a systemd unit at result/lib/systemd/system/touchegg.service with the following content:


[Unit]
Description=Touchégg Daemon
Documentation=https://github.com/JoseExposito/touchegg/tree/master/installation#readme

[Service]
Type=simple
Group=input
ExecStart=/usr/bin/touchegg --daemon
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

How would you go about adding this service to NixOS?

And if you don’t mind, I also have another question. Do you think these changes would be suitable for upstream touchegg?

Thanks again! :slight_smile:

The service needs to be handled through the configure_file treatment. There at least it should work just fine at least.

After you fix the path, you can add the package to systemd.packages options in your configuration.nix and that will enable it.

Yes, these changes are very suitable for upstream. I always encourage people to make compatibility changes upstreamable so that Nixpkgs is simpler and other distros can benefit from the changes too.

Great thanks! I think everything is working properly now.

I will try send a PR upstream to touchegg and then try to make a PR for nixpkgs after. Thank you so much for the help!