# How to release an extension

**URL:** <https://discourse.slicer.org/t/how-to-release-an-extension/26289>\
**Category:** Development\
**Tags:** extensions-manager, scripted, extensions\
**Created:** [November 17, 2022, 6:13pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289 "2022-11-17T18:13:49Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ebrahim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ebrahim/32/13403_2.png) [@ebrahim](https://discourse.slicer.org/u/ebrahim)\
**Post date:** [November 17, 2022, 6:13pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/1 "2022-11-17T18:13:49Z")

</div>

When creating a purely scripted (i.e. python only) Slicer extension, what is a good release “object” to create and how do I create it?

Ideally I want something that I can use with the extension manager’s _Install from file_ button

![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/5/3/5353a2dd81293e647e00711d228bab1270dc30e0.png)

I see it’s looking for some kind of zip or tarball, but IDK what exactly

---

<div class="post-metadata">

**Author:** ![jcfr](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/jcfr/32/17825_2.png) [@jcfr](https://discourse.slicer.org/u/jcfr)\
**Post date:** [November 17, 2022, 7:45pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/2 "2022-11-17T19:45:59Z")

</div>

> what is a good release “object” to create and how do I create it?  
> for some kind of zip or tarball, but IDK what exactly

These `.tar.gz` or `.zip` files correspond to the extension package generated by building the extension. See [Developer Guide/ Extensions Build an extension](https://slicer.readthedocs.io/en/latest/developer_guide/extensions.html#build-an-extension)

Usually, the official Slicer build machine take care of generting the “package” and uploading it to [https://slicer-packages.kitware.com/](https://slicer-packages.kitware.com/)

> start Slicer with your python module

This can be done specifying an additional module path. See [Application settings — 3D Slicer documentation](https://slicer.readthedocs.io/en/latest/user_guide/settings.html#additional-module-paths)

---

<div class="post-metadata">

**Author:** ![ebrahim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ebrahim/32/13403_2.png) [@ebrahim](https://discourse.slicer.org/u/ebrahim)\
**Post date:** [November 17, 2022, 8:05pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/3 "2022-11-17T20:05:49Z")

</div>

Thanks. So the extension manager’s _Install from file_ button cannot be used to install a purely scripted module? It has to be done by specifying an additional module path?

---

<div class="post-metadata">

**Author:** ![Sam\_Horvath](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/sam_horvath/32/3092_2.png) [@Sam\_Horvath](https://discourse.slicer.org/u/Sam_Horvath)\
**Post date:** [November 17, 2022, 8:14pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/4 "2022-11-17T20:14:56Z")

</div>

The install from file button is intended to install extension packages, which are as @jcfr says are the tar/zip files. When we distribute extensions on the factory machines, pure python extensions are still “built” i.e. cmake and the build system are used to copy and zip the files into the appropriate structure.

The best and most repeatable way to generate the release object is to build and package the extension so that it is compatible with the Slicer extension ecosystem.

---

<div class="post-metadata">

**Author:** ![Sam\_Horvath](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/sam_horvath/32/3092_2.png) [@Sam\_Horvath](https://discourse.slicer.org/u/Sam_Horvath)\
**Post date:** [November 17, 2022, 8:26pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/5 "2022-11-17T20:26:02Z")

</div>

Here is an example of a “pure python” extension that is built on the factory: [GitHub - acetylsalicyl/SlicerRawImageGuess](https://github.com/acetylsalicyl/SlicerRawImageGuess)

The important thing is that it must be an extension (and have the appropriate CMake files), not just a module file.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [November 17, 2022, 9:25pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/6 "2022-11-17T21:25:01Z")

</div>

And here is the extension package created from the RawImageGuess extension by building the PACKAGE target: [https://slicer-packages.kitware.com/api/v1/item/62871f65e8408647b39feaba/download](https://slicer-packages.kitware.com/api/v1/item/62871f65e8408647b39feaba/download)

As you can see, the files are mostly just copied from the source tree, but [exactly which files are included in the package and where they are placed](https://github.com/acetylsalicyl/SlicerRawImageGuess/blob/500aff32eeb3515e20508354e6bccd5bde787a48/RawImageGuess/CMakeLists.txt#L5-L12) and [extension metadata](https://github.com/acetylsalicyl/SlicerRawImageGuess/blob/500aff32eeb3515e20508354e6bccd5bde787a48/CMakeLists.txt#L7-L13) (that is saved in the s4ext file in the package) are specified in CMakeLists.txt files, and the package structure is slightly different on macOS than on Linux and Windows.

It would be nice to build extension package from Python-only extensions without having to build Slicer. There could be several options:

- Option A: Use the build tree in [slicer/slicer-base docker image](https://github.com/slicer/SlicerDocker). It may be just a single-line command that you need to write to build a package from your extension’s source tree. Unfortunately, it would only create Linux package.
- Option B: Create some sham CMake files that would provide an empty `Slicer` package for `find_package` and various CMake macros and functions that are used for building an extension. Then CMake could be pip-installed and used for building the package, without a real Slicer build tree and without the need for any additional build tools.
- Option C: Create an alternative packaging method for Python-only packages. It could be based on a manifest file that would specify extension metadata and list of files to include in the package. We would then need 1. some Python scripts that could create an extension package based on this manifest, and 2. CMake functions, which would allow building and testing the extension based on information in the manifest file.

---

<div class="post-metadata">

**Author:** ![ebrahim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/ebrahim/32/13403_2.png) [@ebrahim](https://discourse.slicer.org/u/ebrahim)\
**Post date:** [November 18, 2022, 3:38pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/7 "2022-11-18T15:38:10Z")

</div>

Thank you everyone for the responses. So in the end it _is_ possible to install a purely scripted extension using the “Install from file” button. Piecing it together from the replies above, here is what finally worked for me:

```nohighlight
mkdir build
cd build
cmake .. -DSlicer_DIR=path/to/my/slicer/inner/build -DCMAKE_BUILD_TYPE:STRING=Release
make package

```

This created a tar.gz file that I was then able to use with the “Install from file” button.

(This is sort of explained in a scattered way in the [extension guide](https://slicer.readthedocs.io/en/latest/developer_guide/extensions.html), but was not very straight forward to tease out for me, partly because I have a poor understanding of build systems 😅 , so ty for the help)

---

<div class="post-metadata">

**Author:** ![Joshua\_Jacobs1](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/joshua_jacobs1/32/12645_2.png) [@Joshua\_Jacobs1](https://discourse.slicer.org/u/Joshua_Jacobs1)\
**Post date:** [May 11, 2023, 7:22pm UTC](https://discourse.slicer.org/t/how-to-release-an-extension/26289/8 "2023-05-11T19:22:10Z")

</div>

Ebrahim,

I used the following script to create installation packages for Windows, Mac, and Linux from the created package for a linux machine.

```auto
#!/bin/bash
build_dir=../Code/slicerflywheelcaseiterator/build
cp $build_dir/*git*tar.gz ./

# 30822: Slicer-5.0.2
# 30893: Slicer-5.0.3
# 31122: Slicer-5.1.0 (draft)
# 31314: Slicer-5.2.0
# 31317: Slicer-5.2.1
# 31382: Slicer-5.2.2
# 31734: Slicer-5.3.0

declare -A slicer_versions=(
    [5.0.2]="30822"
    [5.0.3]="30893"
    [5.1.0]="31122" # draft
    [5.2.0]="31314"
    [5.2.1]="31317"
    [5.2.2]="31382"
    [5.3.0]="31734"
)

linux_build_tar=*git*.tar.gz
linux_build=`basename --suffix=.tar.gz ${linux_build_tar%%.*}`
ext_num=`echo $linux_build | awk -F'-' '{print $1}'`
ext_name=`echo $linux_build | awk -F'64-' '{print $2}'| awk -F'-git' '{print $1}'`
slicer_base_version=5.3

echo $linux_build_tar
echo $linux_build
echo $ext_num
echo $ext_name

tar xzvf $linux_build_tar

for i in 0 1 2;do
    cp -rf $linux_build/lib/Slicer-$slicer_base_version $linux_build/lib/Slicer-5.$i;
    cp -rf $linux_build/share/Slicer-$slicer_base_version $linux_build/share/Slicer-5.$i;
done

tar czvf $linux_build_tar $linux_build

# create windows build
windows_build=${linux_build/linux/win}
cp -rf $linux_build $windows_build

tar czvf $windows_build.tar.gz $windows_build

## create mac build
mac_build=${linux_build/linux/mac}
for key in "${!slicer_versions[@]}"; do
    slicer_dest_version=${key:0:3}
    dest_ext_num=${slicer_versions[$key]}
    mac_dir=$mac_build/Slicer.app/Contents/Extensions-$dest_ext_num/$ext_name
    echo $mac_dir
    mkdir -p $mac_dir/{lib,share}
    cp -rf $linux_build/lib/Slicer-$slicer_base_version \
           $mac_dir/lib/Slicer-$slicer_dest_version
    cp -rf $linux_build/share/Slicer-$slicer_base_version \
           $mac_dir/share/Slicer-$slicer_dest_version       
done

zip -r $mac_build.zip $mac_build

# # clean up
rm -rf $linux_build
rm -rf $windows_build
rm -rf $mac_build

```
