Unverified Commit d6a0449d authored by Pol Dellaiera's avatar Pol Dellaiera Committed by GitHub
Browse files

freecad: make customizable (#347776)

parents 76193bc2 a7ab6aa5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -898,6 +898,8 @@

- `virtualisation.incus` module gained new `incus-user.service` and `incus-user.socket` systemd units. It is now possible to add a user to `incus` group instead of `incus-admin` for increased security.

- `freecad` now supports addons and custom configuration in nix-way, which can be used by calling `freecad.customize`.

## Detailed Migration Information {#sec-release-24.11-migration}

### `sound` options removal {#sec-release-24.11-migration-sound}
+149 −0
Original line number Diff line number Diff line
From 23ddb6ff148ec5c27da050ba0eb7a2e449b8450b Mon Sep 17 00:00:00 2001
From: Yury Shvedov <yury.shvedov@kaspersky.com>
Date: Mon, 4 Nov 2024 14:22:22 +0300
Subject: [PATCH] Gui: take in account module-path argument

Use paths passed with `--module-path` argument to search for preference
packs

Change-Id: If168dbd99a826757290ee6b918f5b712305fe2bb
---
 src/Gui/DlgPreferencePackManagementImp.cpp | 16 +++++----
 src/Gui/PreferencePackManager.cpp          | 39 +++++++++++++++++-----
 src/Gui/PreferencePackManager.h            |  5 +++
 3 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/src/Gui/DlgPreferencePackManagementImp.cpp b/src/Gui/DlgPreferencePackManagementImp.cpp
index a1a0dad41a..50f3982f21 100644
--- a/src/Gui/DlgPreferencePackManagementImp.cpp
+++ b/src/Gui/DlgPreferencePackManagementImp.cpp
@@ -54,7 +54,7 @@ void DlgPreferencePackManagementImp::showEvent(QShowEvent* event)
     // but can only disable individual installed packs (though we can completely uninstall the pack's
     // containing Addon by redirecting to the Addon Manager).
     auto savedPreferencePacksDirectory = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks";
-    auto modDirectory = fs::path(App::Application::getUserAppDataDir()) / "Mod";
+    auto modDirectories = Application::Instance->prefPackManager()->modPaths();
     auto resourcePath = fs::path(App::Application::getResourceDir()) / "Gui" / "PreferencePacks";
 
     // The displayed tree has two levels: at the toplevel is either "User-Saved Packs" or the name
@@ -66,12 +66,14 @@ void DlgPreferencePackManagementImp::showEvent(QShowEvent* event)
     auto builtinPacks = getPacksFromDirectory(resourcePath);
 
     std::map<std::string, std::vector<std::string>> installedPacks;
-    if (fs::exists(modDirectory) && fs::is_directory(modDirectory)) {
-        for (const auto& mod : fs::directory_iterator(modDirectory)) {
-            auto packs = getPacksFromDirectory(mod);
-            if (!packs.empty()) {
-                auto modName = mod.path().filename().string();
-                installedPacks.emplace(modName, packs);
+    for (const auto& modDirectory : modDirectories) {
+        if (fs::exists(modDirectory) && fs::is_directory(modDirectory)) {
+            for (const auto& mod : fs::directory_iterator(modDirectory)) {
+                auto packs = getPacksFromDirectory(mod);
+                if (!packs.empty()) {
+                    auto modName = mod.path().filename().string();
+                    installedPacks.emplace(modName, packs);
+                }
             }
         }
     }
diff --git a/src/Gui/PreferencePackManager.cpp b/src/Gui/PreferencePackManager.cpp
index dfc54240c0..83e32fa05e 100644
--- a/src/Gui/PreferencePackManager.cpp
+++ b/src/Gui/PreferencePackManager.cpp
@@ -30,6 +30,7 @@
 #endif
 
 #include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
 #include <QDir>
 
 #include "PreferencePackManager.h"
@@ -134,12 +135,11 @@ void PreferencePack::applyConfigChanges() const
 }
 
 PreferencePackManager::PreferencePackManager()
+    : _preferencePackPaths(modPaths())
 {
-    auto modPath = fs::path(App::Application::getUserAppDataDir()) / "Mod";
     auto savedPath = fs::path(App::Application::getUserAppDataDir()) / "SavedPreferencePacks";
     auto resourcePath = fs::path(App::Application::getResourceDir()) / "Gui" / "PreferencePacks";
-    _preferencePackPaths.push_back(resourcePath);
-    _preferencePackPaths.push_back(modPath);
+    _preferencePackPaths.insert(_preferencePackPaths.begin(), resourcePath);
     _preferencePackPaths.push_back(savedPath);
     rescan();
 
@@ -232,6 +232,26 @@ void Gui::PreferencePackManager::importConfig(const std::string& packName,
     rescan();
 }
 
+// TODO(Shvedov): Is this suitable place for this method? It is more generic,
+// and maybe more suitable place at Application?
+std::vector<boost::filesystem::path> Gui::PreferencePackManager::modPaths() const
+{
+    auto userModPath = fs::path(App::Application::getUserAppDataDir()) / "Mod";
+
+    auto& config = App::Application::Config();
+    auto additionalModules = config.find("AdditionalModulePaths");
+    std::vector<boost::filesystem::path> result;
+
+    if (additionalModules != config.end()) {
+        boost::split(result,
+                     additionalModules->second,
+                     boost::is_any_of(";"),
+                     boost::token_compress_on);
+    }
+    result.emplace_back(userModPath);
+    return result;
+}
+
 void Gui::PreferencePackManager::FindPreferencePacksInPackage(const fs::path &mod)
 {
     try {
@@ -528,7 +548,6 @@ std::vector<PreferencePackManager::TemplateFile> PreferencePackManager::template
     // (alternate spellings are provided for packages using CamelCase and snake_case, and both major English dialects)
 
     auto resourcePath = fs::path(App::Application::getResourceDir()) / "Gui";
-    auto modPath = fs::path(App::Application::getUserAppDataDir()) / "Mod";
 
     std::string group = "Built-In";
     if (fs::exists(resourcePath) && fs::is_directory(resourcePath)) {
@@ -536,11 +555,13 @@ std::vector<PreferencePackManager::TemplateFile> PreferencePackManager::template
         std::copy(localFiles.begin(), localFiles.end(), std::back_inserter(_templateFiles));
     }
 
-    if (fs::exists(modPath) && fs::is_directory(modPath)) {
-        for (const auto& mod : fs::directory_iterator(modPath)) {
-            group = mod.path().filename().string();
-            const auto localFiles = scanForTemplateFiles(group, mod);
-            std::copy(localFiles.begin(), localFiles.end(), std::back_inserter(_templateFiles));
+    for (const auto& modPath : modPaths()) {
+        if (fs::exists(modPath) && fs::is_directory(modPath)) {
+            for (const auto& mod : fs::directory_iterator(modPath)) {
+                group = mod.path().filename().string();
+                const auto localFiles = scanForTemplateFiles(group, mod);
+                std::copy(localFiles.begin(), localFiles.end(), std::back_inserter(_templateFiles));
+            }
         }
     }
 
diff --git a/src/Gui/PreferencePackManager.h b/src/Gui/PreferencePackManager.h
index 301e160df2..e5776e47a0 100644
--- a/src/Gui/PreferencePackManager.h
+++ b/src/Gui/PreferencePackManager.h
@@ -191,6 +191,11 @@ namespace Gui {
          */
         void importConfig(const std::string &packName, const boost::filesystem::path &path);
 
+        /**
+         * Get a list of all mod directories.
+         */
+        std::vector<boost::filesystem::path> modPaths() const;
+
     private:
 
         void FindPreferencePacksInPackage(const boost::filesystem::path& mod);
-- 
2.44.1
+98 −0
Original line number Diff line number Diff line
{
  runCommand,
  buildEnv,
  makeWrapper,
  lib,
  python311,
  writeShellScript,
}:
let
  wrapPathsStr =
    flag: values:
    builtins.concatStringsSep " " (
      builtins.concatMap (p: [
        "--add-flags"
        flag
        "--add-flags"
        p
      ]) values
    );

  wrapCfgStr =
    typ: val:
    let
      installer = writeShellScript "insteller-${typ}" ''
        dst="$HOME/.config/FreeCAD/${typ}.cfg"
        if [ ! -f "$dst" ]; then
          mkdir -p "$(dirname "$dst")"
          cp --no-preserve=mode,ownership '${val}' "$dst"
        fi
      '';
    in
    lib.optionalString (val != null) "--run ${installer}";

  pythonsProcessed = builtins.map (
    pyt:
    if builtins.isString pyt then
      pyt
    else if builtins.isFunction pyt then
      "${(python311.withPackages pyt)}/lib/python3.11/site-packages"
    else
      throw "Expected string or function as python paths for freecad"
  );

  makeCustomizable =
    freecad:
    freecad
    // {
      customize =
        {
          name ? freecad.name,
          modules ? [ ],
          pythons ? [ ],
          makeWrapperFlags ? [ ],
          userCfg ? null,
          systemCfg ? null,
        }:
        let
          modulesStr = wrapPathsStr "--module-path" modules;
          pythonsStr = wrapPathsStr "--python-path" (pythonsProcessed pythons);
          makeWrapperFlagsStr = builtins.concatStringsSep " " (builtins.map (f: "'${f}'") makeWrapperFlags);

          userCfgStr = wrapCfgStr "user" userCfg;
          systemCfgStr = wrapCfgStr "system" systemCfg;

          bin = runCommand "${name}-bin" { nativeBuildInputs = [ makeWrapper ]; } ''
            mkdir -p "$out/bin"
            for exe in FreeCAD{,Cmd}; do
              if [[ ! -e ${freecad}/bin/$exe ]]; then
                echo "No binary $exe in freecad package"
                false
              fi
              dest="$out/bin/$exe";
              makeWrapper "${freecad}/bin/$exe" "$dest" \
                --inherit-argv0                         \
                ${modulesStr}                           \
                ${pythonsStr}                           \
                ${userCfgStr}                           \
                ${systemCfgStr}                         \
                ${makeWrapperFlagsStr}
            done
            ln -s FreeCAD $out/bin/freecad
            ln -s FreeCADCmd $out/bin/freecadcmd
          '';
        in
        makeCustomizable (buildEnv {
          inherit name;
          paths = [
            (lib.lowPrio freecad)
            bin
          ];
        });
      override = f: makeCustomizable (freecad.override f);
      overrideAttrs = f: makeCustomizable (freecad.overrideAttrs f);
    };
in
{
  inherit makeCustomizable;
}
+6 −20
Original line number Diff line number Diff line
{ lib
, callPackage
, cmake
, coin3d
, doxygen
, eigen
, fetchFromGitHub
, fmt
, freecad
, gfortran
, gts
, hdf5
@@ -22,7 +22,6 @@
, opencascade-occt_7_6
, pkg-config
, python311Packages
, runCommand  # for passthru.tests
, spaceNavSupport ? stdenv.hostPlatform.isLinux
, stdenv
, swig
@@ -60,8 +59,9 @@ let
    scipy
    shiboken2
    ;
  freecad-utils = callPackage ./freecad-utils.nix { };
in
stdenv.mkDerivation (finalAttrs: {
freecad-utils.makeCustomizable (stdenv.mkDerivation (finalAttrs: {
  pname = "freecad";
  version = "1.0rc4";

@@ -131,6 +131,7 @@ stdenv.mkDerivation (finalAttrs: {
  patches = [
    ./0001-NIXOS-don-t-ignore-PYTHONPATH.patch
    ./0002-FreeCad-OndselSolver-pkgconfig.patch
    ./0003-Gui-take-in-account-module-path-argument.patch
  ];

  cmakeFlags = [
@@ -174,22 +175,7 @@ stdenv.mkDerivation (finalAttrs: {
    ln -s $out/bin/FreeCADCmd $out/bin/freecadcmd
  '';

  passthru.tests = {
    # Check that things such as argument parsing still work correctly with
    # the above PYTHONPATH patch. Previously the patch used above changed
    # the `PyConfig_InitIsolatedConfig` to `PyConfig_InitPythonConfig`,
    # which caused the built-in interpreter to attempt (and fail) to doubly
    # parse argv. This should catch if that ever regresses and also ensures
    # that PYTHONPATH is still respected enough for the FreeCAD console to
    # successfully run and check that it was included in `sys.path`.
    python-path =
      runCommand "freecad-test-console"
        {
          nativeBuildInputs = [ freecad ];
        } ''
        HOME="$(mktemp -d)" PYTHONPATH="$(pwd)/test" FreeCADCmd --log-file $out -c "if not '$(pwd)/test' in sys.path: sys.exit(1)" </dev/null
      '';
  };
  passthru.tests = callPackage ./tests {};

  meta = {
    homepage = "https://www.freecad.org";
@@ -214,4 +200,4 @@ stdenv.mkDerivation (finalAttrs: {
    maintainers = with lib.maintainers; [ gebner srounce ];
    platforms = lib.platforms.linux;
  };
})
}))
+7 −0
Original line number Diff line number Diff line
{
  callPackage,
}:
{
  python-path = callPackage ./python-path.nix { };
  modules = callPackage ./modules.nix { };
}
Loading