1

I have made an addon that when installed on Windows works perfectly fine. But when installed on Blender running on Mac OS, the binary contained within the addon zip file loses the execute permission. Here to prove that the execute permissions in -rwxrwxrwx@ were there before installation, when zip was downloaded and extracted into /Users/user/Downloads/:

user@macbook % pwd
/Users/user/Downloads/addon-name/executables/0.0.1/darwin/myexe.app/Contents/MacOS
user@macbook % ls -la
total 8840
drwxrwxrwx@ 3 user  staff       96 Jan 19 09:46 .
drwxrwxrwx@ 6 user  staff      192 Jan 19 09:46 ..
-rwxrwxrwx@ 1 user  staff  4523992 Jan 18 01:19 executable-binary

But when I install the addon zip file in Blender running on Mac OS and check the directory /Users/user/Library/Application Support/Blender/4.0/scripts/addons/ where Blender puts the addon, I noticed that the binary has lost its execute permission in -rw-r--r-- and that's when my addon fails to function properly.

user@macbook % pwd
/Users/user/Library/Application Support/Blender/4.0/scripts/addons/addon-name/executables/0.0.1/darwin/myexe.app/Contents/MacOS
user@macbook % ls -la                   
total 8840
drwxr-xr-x  3 user  staff       96 Jan 19 14:26 .
drwxr-xr-x  6 user  staff      192 Jan 19 14:26 ..
-rw-r--r--  1 user  staff  4523992 Jan 19 14:26 executable-binary

My current workaround is to manually add the execute permissions to the binary using chmod +x executable-binary. All the other parent directories still have retained the execute permissions. It's just the binary that has lost it. I'm uncertain whether this behavior is a bug or intentional, but it seems unlikely to be intentional since it results in inconsistency, especially considering that it functions correctly on Windows. So my question is how do I properly deploy and install such an addon that uses third party executable binaries? So that I can properly communicate that to the users via documentation.

vklidu
  • 36,165
  • 1
  • 61
  • 135
Harry McKenzie
  • 10,995
  • 8
  • 23
  • 51
  • 1
    Can confirm and I personally guess this is intentional to prevent any execution of malware but I may be wrong... so I'd suggest to report this behavior to make sure. In the meantime, you can make it executable automatically in the register() call, see my basic setup (tested on 4.0) based on a rather old answer. Does this help? – p2or Jan 23 '24 at 16:02
  • @p2or hi thank you for looking into this! interesting i totally forgot to check if there was a script. but wouldn't os.chmod(executable_path, 0o755) be enough? – Harry McKenzie Jan 24 '24 at 07:06
  • i think this is the solution/workaround to my issue. can you add that as an answer. in the meantime i will borrow a mac to test this. but i guess if this works on Windows Ubuntu app then it must definitely work on Mac i suppose? – Harry McKenzie Jan 24 '24 at 07:16
  • 1
    No problem. Yep, os.chmod(app, 0o755) works as well (tested on OSX), just wanted to use 'Pathlib' and provide a cross-platform solution: https://docs.python.org/3/library/pathlib.html#pathlib.Path.chmod – p2or Jan 24 '24 at 07:30
  • awesome thank you so much for the save! please add as answer and i will award the bounty and i can close this question :) – Harry McKenzie Jan 24 '24 at 07:47

1 Answers1

1

The reason for this issue can be found in scripts/startup/bl_operators/userpref.py.

Addons provided as zip files are extracted using ZipFile.extractall(path=None, ...), see line 688. As part of the extraction process, shutil.copyfileobj() is getting called internally, which copies each file to the destination in an exemplary manner, but unfortunately without the execution bits.


However, you can make the file executable again within the register() call of your addon by using either the pathlib or the os module in combination with the stat module:

...

Path to the app

from pathlib import Path app = Path(file).parent / "name-of-executable"

classes = ( WM_OT_HelloWorld, WM_PT_HelloWorld, ... )

def register(): from bpy.utils import register_class for cls in classes: register_class(cls)

import stat
app.chmod(app.stat().st_mode | stat.S_IEXEC)
#os.chmod(str(app), st.st_mode | stat.S_IEXEC)

def unregister(): from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls)

Full demo to capture the output of a C++ program supplied by an addon, based on Use array values from a C++ program in blender (you just need to compile random-numbers.cpp and zip both files).

p2or
  • 15,860
  • 10
  • 83
  • 143