VCF Automation Blog

from Stefan Schnell


A shared object is a Linux library which contains code and/or data that can be used between multiple processes. They are binary files and similar to Dynamic Link Libraries (DLLs) in Windows. When a Linux program needs a shared object, it loads it into memory and uses its code and/or data. Shared objects are built with compilers like C or C++. This post shows how shared objects can be used in VCF Automation with the Python runtime environment.

Use Shared Object with Python


For my first experiment I am using the PureBasic programming language, which is available for Linux, Windows and OS X. However, any other programming language can also be used that supports the building of shared objects. The source code is very easy to understand. Three functions are exposed. The first adds two integer numbers, the second subtracts two integer numbers, the third returns a hello world string, but it only works with Windows, and the fourth returns also a hello world string, and that works with Linux and Windows.

ProcedureDLL.i Add(x.i, y.i)
  ProcedureReturn x + y
EndProcedure

ProcedureDLL.l Sub(x.l, y.l)
  ProcedureReturn x - y
EndProcedure

ProcedureDLL.s HelloWindows(name.s)
  If Trim(name) = #Empty$
    ProcedureReturn "Hello world from PureBasic"
  Else
    ProcedureReturn "Hello " + name + " from PureBasic"
  EndIf
EndProcedure

ProcedureDLL.i Hello(name.s)
  
  _name.s = PeekS(@name, -1, #PB_UTF8)
  
  retValue.s = #Empty$
  If Trim(_name) = #Empty$
    retValue = "Hello world from PureBasic"
  Else
    retValue = "Hello " + _name + " from PureBasic"
  EndIf
  
  *retValue = AllocateMemory(Len(retValue) + 1)
  PokeS(*retValue, retValue, Len(retValue), #PB_UTF8)
  
  ProcedureReturn *retValue
  
EndProcedure

I assume that the source code was saved with the name libPython.pb.

With the command
./pbcompiler libPython.pb -so libPython.so
the source code is compiled into a shared object.

Now let's take a look at the Python source code that loads the library and executes its functions.
This code is also very easy to understand. The library is loaded, then the first function Add is executed with the numbers 10 and 1. Then the second function Sub is executed with the same numbers. The third function is only called if this program is executed in a Windows environment, which is not the case here. A name is passed to the fourth function and it returns a hello world string. The result is returned as a Python dictionary respectively as VCF Automation Properties.

"""
@module de.stschnell

@version 0.1.0

@runtime python:3.10

@memoryLimit 256000000

@timeout 360

@outputType Properties

Checked with Aria Automation 8.12.0 and 8.16.2
"""
import json
import os.path
import platform
from ctypes import*

def handler(context, inputs):

    result = {}

    try:

        dllName = os.path.dirname(os.path.abspath(__file__))
        if platform.system() == "Windows":
            dllName += "\\libPython.dll"
        elif platform.system() == "Linux":
            dllName += "/libPython.so"
        libPython = cdll.LoadLibrary(dllName)

        libPython.Add.argtypes = [ c_int64, c_int64 ]
        libPython.Add.restype = c_int64
        resultAdd = libPython.Add(10,1)
        result["Addition"] = str(resultAdd)

        libPython.Sub.argtypes = [ c_long, c_long ]
        libPython.Sub.restype = c_long
        resultSub = libPython.Sub(10,1)
        result["Substraction"] = str(resultSub)

        if platform.system() == "Windows":
            libPython.HelloWindows.argtypes = [ c_wchar_p ]
            libPython.HelloWindows.restype = c_wchar_p
            name = c_wchar_p("Stefan")
            resultHelloWindows = libPython.HelloWindows(name)
            result["HelloWindows"] = resultHelloWindows

        libPython.Hello.argtypes = [ c_char_p ]
        libPython.Hello.restype = c_char_p
        name = "Stefan".encode("utf-8")
        resultHello = libPython.Hello(name)
        result["Hello"] = str(resultHello, "utf-8")

        outputs = {
            "status": "done",
            "result": result
        }

    except Exception as err:

        outputs = {
            "status": "incomplete",
            "error": repr(err),
            "result": result
        }

    return outputs

The files must now be combined in a zip file.

vcf automation python zip file with handler.py and shared object files
This zip file can now be imported as an action.

vcf automation python example which uses a shared object
After we have executed the action, we see the correct and expected results. For addition 10 plus 1 equals 11, for subtraction 10 minus 1 equals 9 and the hello world message.

Conclusion

The use of shared libraries or shared objects is very easy with the Python runtime environment. On this way it is also possible to use more extensive functionalities and possibilities of other programming languages in the context of VCF Automation. Cross-platform development is also easily possible. In this example the library was built as a Windows Dynamic Link Library (DLL) and as a Photon OS Shared Object (SO). The source code was compiled unchanged and then used with different Python versions on the different operating systems.

Addendum

Install PureBasic in a Photon Container on Red Hat Linux

  1. Load the Photon image from the repository.
    podman pull photon:4.0

  2. Runs an interactive process in a new container
    podman run -it --name photon photon:4.0

  3. From another terminal copy PureBasic to the Photon container.
    podman cp ./PureBasic_Linux2_X64_LTS_6.12.tar photon:/home

  4. Install the necessary packages in the Photon container.
    tdnf install gcc glibc-devel binutils vim phyton3
    Hint: The Vim editor and Python are not necessary, but they can be used to edit the PureBasic source code or to test the shared object, which can be very helpful.

  5. Extract the archive in the home directory of the Photon container.
    tar -xf ./PureBasic_Linux2_X64_LTS_6.12.tar

  6. Set in the Photon container the PUREBASIC_HOME environment variable.
    export PUREBASIC_HOME=/home/purebasic

  7. Check the PureBasic compiler.
    ./purebasic/compilers/pbcompiler

    terminal window of photon with purebasic compiler call
Here you can find the corresponding post in PureBasic forum.