Skip to content

Instantly share code, notes, and snippets.

@qingfengxia
Last active March 10, 2022 06:02
Show Gist options
  • Save qingfengxia/3644d651dc9c4146493b14ba95669143 to your computer and use it in GitHub Desktop.
Save qingfengxia/3644d651dc9c4146493b14ba95669143 to your computer and use it in GitHub Desktop.
###########################################################################
# Copyright (c) 2020 Qingfeng Xia <qingfeng.xia_ukaea.uk> #
# #
# This file is part of the FreeCAD CAx development system. #
# #
# This library is free software; you can redistribute it and/or #
# modify it under the terms of the GNU Library General Public #
# License as published by the Free Software Foundation; either #
# version 2 of the License, or (at your option) any later version. #
# #
# This library is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU Library General Public License for more details. #
# #
# You should have received a copy of the GNU Library General Public #
# License along with this library; see the file COPYING.LIB. If not, #
# write to the Free Software Foundation, Inc., 59 Temple Place, #
# Suite 330, Boston, MA 02111-1307, USA #
# #
###########################################################################
"""
this python3 only script works for all platforms if freecad is on command line PATH
on windows, if freecad is not on PATH , nor uninstall record found in registry
then PYTHONPATH must be set
usage:
```python
from detectFreeCAD import append_freecad_mod_path
append_freecad_mod_path()
try:
import FreeCAD
except ImportError:
print("freecad is not installed or detectable, exit from this script")
sys.exit(0)
```
"""
import sys
import subprocess
from shutil import which
import platform
import os.path
def is_executable(name):
"""Check whether `name` is on PATH and marked as executable.
for python3 only, but cross-platform"""
# from whichcraft import which
return which(name) is not None
def detect_lib_path(out, libname):
"""parse ldd output and extract the lib, POSIX only
OSX Dynamic library naming: lib<libname>.<soversion>.dylib
"""
# print(type(out))
output = out.decode("utf8").split("\n")
for l in output:
if l.find(libname) >= 0:
# print(l)
i_start = l.find("=> ") + 3
i_end = l.find(" (") + 1
lib_path = l[i_start:i_end]
return lib_path
print("dynamic lib file is not found, check the name (without suffix)")
def get_lib_dir(program, libname):
program_full_path = which(program)
# print(program_full_path)
if is_executable("ldd"):
process = subprocess.Popen(
["ldd", program_full_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
out, err = process.communicate()
solib_path = detect_lib_path(out, libname)
lib_path = os.path.dirname(solib_path)
if os.path.exists(lib_path):
return lib_path
else:
print("library file " + libname + " is found, but lib dir does not exist")
else:
print("ldd is not available, it is not posix OS")
# macos: has no ldd, https://stackoverflow.com/questions/45464584/macosx-which-dynamic-libraries-linked-by-binary
def get_freecad_app_name():
""
if is_executable("freecad"): # debian
return "freecad"
elif is_executable("FreeCAD"): # fedora
return "FreeCAD"
elif is_executable("freecad-daily"):
return "freecad-daily"
else:
print("FreeCAD is not installed or set on PATH")
return None
def get_freecad_lib_path():
""
os_name = platform.system()
fc_name = get_freecad_app_name()
if os_name == "Linux":
if fc_name:
return get_lib_dir(fc_name, "libFreeCADApp")
else:
return None
elif os_name == "Windows":
return get_lib_path_on_windows(fc_name)
elif os_name == "Darwin":
raise NotImplementedError("MACOS is not supported yet")
else:
# assuming POSIX platform with ldd command
return get_lib_dir(fc_name, "libFreeCADApp")
def get_lib_path_on_windows(fc_name):
# windows installer has the options add freecad to PYTHONPATH
# this can also been done manually afterward, settting env variable PYTHONPATH
# the code below assuming freecad is on command line search path
if fc_name:
fc_full_path = which(fc_name)
lib_path = os.path.dirname(os.path.dirname(fc_full_path)) + os.path.sep + "lib"
if os.path.exists(lib_path + os.path.sep + "Part.pyd"):
return lib_path
else: # check windows registry key, if installed by installer
get_installaton_path_on_windows("FreeCAD")
def get_installaton_path_on_windows(fc_name):
# tested for FreeCAD 0.18
import itertools
from winreg import (
ConnectRegistry,
HKEY_LOCAL_MACHINE,
OpenKeyEx,
QueryValueEx,
CloseKey,
KEY_READ,
EnumKey,
WindowsError,
)
try:
root = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
reg_path = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
akey = OpenKeyEx(root, reg_path, 0, KEY_READ)
for i in itertools.count():
try:
subname = EnumKey(akey, i)
except WindowsError:
break
if subname.lower().find(fc_name.lower()) > 0:
subkey = OpenKeyEx(akey, subname, 0, KEY_READ)
pathname, regtype = QueryValueEx(subkey, "InstallLocation")
CloseKey(subkey)
return os.path.expandvars(pathname)
# close key and root
CloseKey(akey)
CloseKey(root)
return None
except OSError:
return None
def append_freecad_mod_path():
try:
import FreeCAD # PYTHONPATH may has been set, do nothing
except ImportError:
# in case PYTHONPATH is not set
cmod_path = get_freecad_lib_path() # c module path
if cmod_path:
pymod_path = os.path.join(cmod_path, os.pardir) + os.path.sep + "Mod"
sys.path.append(cmod_path)
sys.path.append(pymod_path)
if __name__ == "__main__":
print("FreeCAD library path detection result: ", get_freecad_lib_path())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment