all: add wasmedge wasip1 runner

The wasmedge runner provides the wasmedge.org WASI runtime
for testing the wasip1 implementation.

For golang/go#60097

Change-Id: I1148977296d00163f638d4fcc7a916c58c6d17c5
Reviewed-on: https://go-review.googlesource.com/c/build/+/493696
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
Run-TryBot: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/dashboard/builders.go b/dashboard/builders.go
index 653ec19..2607dc1 100644
--- a/dashboard/builders.go
+++ b/dashboard/builders.go
@@ -102,6 +102,7 @@
 	"solaris":               "solaris-amd64-oraclerel",
 	"solaris-amd64":         "solaris-amd64-oraclerel",
 	"wasm":                  "js-wasm-node18",
+	"wasmedge":              "wasip1-wasm-wasmedge",
 	"wasmer":                "wasip1-wasm-wasmer",
 	"wasmtime":              "wasip1-wasm-wasmtime",
 	"wazero":                "wasip1-wasm-wazero",
@@ -330,6 +331,11 @@
 		ContainerImage: "linux-x86-stretch:latest",
 		SSHUsername:    "root",
 	},
+	"host-linux-amd64-wasip1-wasm-wasmedge": {
+		Notes:          "Container with wasmedge for testing wasip1/wasm.",
+		ContainerImage: "wasip1-wasm-wasmedge:latest",
+		SSHUsername:    "root",
+	},
 	"host-linux-amd64-wasip1-wasm-wasmer": {
 		Notes:          "Container with wasmer for testing wasip1/wasm.",
 		ContainerImage: "wasip1-wasm-wasmer:latest",
@@ -3242,6 +3248,37 @@
 			"GO_DISABLE_OUTBOUND_NETWORK=1", "GOWASIRUNTIME=wasmer",
 		},
 	})
+	addBuilder(BuildConfig{
+		Name:        "wasip1-wasm-wasmedge",
+		HostType:    "host-linux-amd64-wasip1-wasm-wasmedge",
+		KnownIssues: []int{60097},
+		buildsRepo: func(repo, branch, goBranch string) bool {
+			b := buildRepoByDefault(repo) && atLeastGo1(goBranch, 21)
+			switch repo {
+			case "benchmarks", "debug", "perf", "talks", "tools", "tour", "website":
+				// Don't test these golang.org/x repos.
+				b = false
+			}
+			if repo != "go" && !(branch == "master" && goBranch == "master") {
+				// For golang.org/x repos, don't test non-latest versions.
+				b = false
+			}
+			return b
+		},
+		distTestAdjust: func(run bool, distTest string, isNormalTry bool) bool {
+			if isNormalTry && (strings.Contains(distTest, "/internal/") || distTest == "reboot") {
+				// Skip some tests in an attempt to speed up normal trybots, inherited from CL 121938.
+				run = false
+			}
+			return run
+		},
+		numTryTestHelpers: 3,
+		env: []string{
+			"GOOS=wasip1", "GOARCH=wasm", "GOHOSTOS=linux", "GOHOSTARCH=amd64",
+			"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/workdir/go/misc/wasm",
+			"GO_DISABLE_OUTBOUND_NETWORK=1", "GOWASIRUNTIME=wasmedge",
+		},
+	})
 }
 
 // addBuilder adds c to the Builders map after doing some checks.
diff --git a/dashboard/builders_test.go b/dashboard/builders_test.go
index 12695a6..682a94d 100644
--- a/dashboard/builders_test.go
+++ b/dashboard/builders_test.go
@@ -668,6 +668,9 @@
 		{b("wasip1-wasm-wasmer", "go"), onlyPost},
 		{b("wasip1-wasm-wasmer@go1.21", "go"), onlyPost},
 		{b("wasip1-wasm-wasmer@go1.20", "go"), none},
+		{b("wasip1-wasm-wasmedge", "go"), onlyPost},
+		{b("wasip1-wasm-wasmedge@go1.21", "go"), onlyPost},
+		{b("wasip1-wasm-wasmedge@go1.20", "go"), none},
 		// Test wasip1/wasm on a subset of golang.org/x repos:
 		{b("wasip1-wasm-wazero", "arch"), onlyPost},
 		{b("wasip1-wasm-wazero", "crypto"), onlyPost},
@@ -705,6 +708,18 @@
 		{b("wasip1-wasm-wasmer", "tools"), none},
 		{b("wasip1-wasm-wasmer", "tour"), none},
 		{b("wasip1-wasm-wasmer", "website"), none},
+		{b("wasip1-wasm-wasmedge", "arch"), onlyPost},
+		{b("wasip1-wasm-wasmedge", "crypto"), onlyPost},
+		{b("wasip1-wasm-wasmedge", "sys"), onlyPost},
+		{b("wasip1-wasm-wasmedge", "net"), onlyPost},
+		{b("wasip1-wasm-wasmedge", "benchmarks"), none},
+		{b("wasip1-wasm-wasmedge", "debug"), none},
+		{b("wasip1-wasm-wasmedge", "mobile"), none},
+		{b("wasip1-wasm-wasmedge", "perf"), none},
+		{b("wasip1-wasm-wasmedge", "talks"), none},
+		{b("wasip1-wasm-wasmedge", "tools"), none},
+		{b("wasip1-wasm-wasmedge", "tour"), none},
+		{b("wasip1-wasm-wasmedge", "website"), none},
 
 		// Race builders. Linux for all, GCE builders for
 		// post-submit, and only post-submit for "go" for
@@ -752,6 +767,7 @@
 		{b("wasip1-wasm-wazero", "exp"), none},
 		{b("wasip1-wasm-wasmtime", "exp"), none},
 		{b("wasip1-wasm-wasmer", "exp"), none},
+		{b("wasip1-wasm-wasmedge", "exp"), none},
 
 		// exp is experimental; it doesn't test against release branches.
 		{b("linux-amd64@go1.99", "exp"), none},
@@ -781,6 +797,7 @@
 		{b("wasip1-wasm-wazero", "build"), none},
 		{b("wasip1-wasm-wasmtime", "build"), none},
 		{b("wasip1-wasm-wasmer", "build"), none},
+		{b("wasip1-wasm-wasmedge", "build"), none},
 		{b("android-386-emu", "build"), none},
 		{b("android-amd64-emu", "build"), none},
 
diff --git a/env/wasip1-wasm-wasmedge/Dockerfile b/env/wasip1-wasm-wasmedge/Dockerfile
new file mode 100644
index 0000000..13dd386
--- /dev/null
+++ b/env/wasip1-wasm-wasmedge/Dockerfile
@@ -0,0 +1,21 @@
+# Copyright 2023 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+ARG REPO
+
+FROM debian:latest as builder
+LABEL maintainer="golang-dev@googlegroups.com"
+
+RUN apt-get update && apt-get -y install python3 git
+
+# A copy of https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.py.
+COPY install.py install.py
+
+RUN python3 install.py --version 0.12.0
+
+FROM ${REPO}/linux-x86-sid:20221109
+
+COPY --from=builder /root/.wasmedge/bin/wasmedge /usr/local/bin/wasmedge
+
+CMD ["/usr/local/bin/stage0"]
diff --git a/env/wasip1-wasm-wasmedge/Makefile b/env/wasip1-wasm-wasmedge/Makefile
new file mode 100644
index 0000000..386325c
--- /dev/null
+++ b/env/wasip1-wasm-wasmedge/Makefile
@@ -0,0 +1,22 @@
+# Copyright 2023 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+IMAGE_NAME=$(shell basename $(CURDIR))
+PROD_REPO=gcr.io/symbolic-datum-552
+
+usage:
+	echo "Use prod or dev targets. For dev, specify your Docker repository with the REPO=foo argument." ; exit 1
+
+prod: Dockerfile
+	docker build -t $(PROD_REPO)/$(IMAGE_NAME):latest --build-arg REPO=$(PROD_REPO) -f Dockerfile .
+
+pushprod: prod
+	docker push $(PROD_REPO)/$(IMAGE_NAME):latest
+
+# You must provide a REPO=your-repo-name arg when you make
+# this target. REPO is the name of the Docker repository
+# that will be prefixed to the name of the image being built.
+dev: Dockerfile
+	docker build -t $(REPO)/$(IMAGE_NAME):latest --build-arg REPO=$(REPO) -f Dockerfile .
+	docker push $(REPO)/$(IMAGE_NAME):latest
diff --git a/env/wasip1-wasm-wasmedge/install.py b/env/wasip1-wasm-wasmedge/install.py
new file mode 100644
index 0000000..b0c2907
--- /dev/null
+++ b/env/wasip1-wasm-wasmedge/install.py
@@ -0,0 +1,1557 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from __future__ import (
+    division,
+    print_function,
+    absolute_import,
+    unicode_literals,
+    with_statement,
+)
+from contextlib import contextmanager
+import shutil
+import sys
+import argparse
+from os.path import expanduser, join, dirname, abspath, exists, islink, lexists, isdir
+from os import (
+    getenv,
+    geteuid,
+    listdir,
+    makedirs,
+    mkdir,
+    readlink,
+    remove,
+    getpid,
+    symlink,
+)
+import tempfile
+import tarfile
+import zipfile
+import platform
+import subprocess
+import re
+import logging
+
+download_url = None
+
+# Define version specific things
+if sys.version_info[0] == 3:
+    import urllib.request
+
+    download_url = urllib.request.urlretrieve
+
+    def reraise(tp, value=None, tb=None):
+        if value is None:
+            value = tp
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+else:
+    exec("def reraise(tp, value=None, tb=None):\n    raise tp, value, tb\n")
+
+    import urllib
+
+    download_url = urllib.urlretrieve
+
+
+def show_progress(block_num, block_size, total_size):
+    downloaded = block_num * block_size
+
+    print(
+        end=(
+            "\r|%-60s|" % ("=" * int(60 * downloaded / (total_size)))
+            + "%6.2f %%" % (downloaded / (total_size) * 100)
+        )
+    )
+
+    if downloaded < total_size:
+        pass
+    else:
+        print("Downloaded")
+
+
+@contextmanager
+def opened_w_error(filename, mode="r"):
+    try:
+        f = open(filename, mode)
+    except IOError as err:
+        logging.critical("Error opening file: %s error: %s", filename, err.strerror)
+        yield None
+    else:
+        try:
+            yield f
+        finally:
+            f.close()
+
+
+def _is_tarxz(filename):
+    return filename.endswith(".tar.xz")
+
+
+def _is_tar(filename):
+    return filename.endswith(".tar")
+
+
+def _is_targz(filename):
+    return filename.endswith(".tar.gz")
+
+
+def _is_tgz(filename):
+    return filename.endswith(".tgz")
+
+
+def _is_zip(filename):
+    return filename.endswith(".zip")
+
+
+def extract_archive(
+    from_path, ipath, to_path=None, remove_finished=False, env_file_path=None
+):
+    files_extracted = []
+
+    if to_path is None:
+        to_path = dirname(from_path)
+
+    if _is_tar(from_path):
+        with tarfile.open(from_path, "r") as tar:
+            tar.extractall(path=to_path)
+            files_extracted = tar.getnames()
+    elif _is_targz(from_path) or _is_tgz(from_path):
+        with tarfile.open(from_path, "r:gz") as tar:
+            tar.extractall(path=to_path)
+            files_extracted = tar.getnames()
+    elif _is_tarxz(from_path):
+        with tarfile.open(from_path, "r:xz") as tar:
+            tar.extractall(path=to_path)
+            files_extracted = tar.getnames()
+    elif _is_zip(from_path):
+        with zipfile.ZipFile(from_path, "r") as z:
+            z.extractall(to_path)
+            files_extracted = z.namelist()
+    else:
+        reraise(ValueError("Extraction of {} not supported".format(from_path)))
+
+    logging.debug("Writing installed files to %s file", env_file_path)
+    with opened_w_error(env_file_path, "a") as env_file:
+        if env_file is not None:
+            for filename in files_extracted:
+                fname = filename.replace(CONST_ipkg, ipath)
+
+                # Skip if it ends with "wasmedge" as it is going to be removed at a later stage
+                if fname.endswith("wasmedge") and not fname.endswith("bin/wasmedge"):
+                    continue
+
+                # replace wasmedge folder name with include
+                if is_default_path(args):
+                    fname = fname.replace("/lib64/", "/" + CONST_lib_dir + "/")
+                if fname.endswith("/lib64"):
+                    fname = fname[:-5] + "lib"
+                if fname.startswith("/usr") and "lib64" in fname:
+                    fname = fname.replace("lib64", "lib", 1)
+                if "Plugin" in fname:
+                    if is_default_path(args):
+                        fname = fname.replace(
+                            join(ipath, CONST_lib_dir, "wasmedge/"), ""
+                        )
+
+                        fname = join(ipath, "plugin", fname)
+                    else:
+                        fname = join(ipath, CONST_lib_dir, "wasmedge", fname)
+                else:
+                    if ipath not in fname:
+                        fname = join(ipath, fname)
+                # replace GNUSparseFile.0 with nothing
+                fname = fname.replace("/GNUSparseFile.0", "")
+                # Don't append system directories
+                if (not is_default_path(args)) and isdir(fname):
+                    continue
+                env_file.write("#" + fname + "\n")
+                logging.debug("Appending:%s", fname)
+        else:
+            logging.warning("Unable to write to env file")
+
+    if remove_finished:
+        remove(from_path)
+
+
+# https://stackoverflow.com/questions/1868714/
+# how-do-i-copy-an-entire-directory-of-files-
+# into-an-existing-directory-using-pyth
+def copytree(src, dst, symlinks=True, ignore=None):
+    if not exists(dst):
+        makedirs(dst)
+        shutil.copystat(src, dst)
+    lst = listdir(src)
+    if ignore:
+        excl = ignore(src, lst)
+        lst = [x for x in lst if x not in excl]
+    for item in lst:
+        s = join(src, item)
+        d = join(dst, item)
+        if symlinks and islink(s):
+            if lexists(d):
+                remove(d)
+            symlink(readlink(s), d)
+        elif isdir(s):
+            copytree(s, d, symlinks, ignore)
+        else:
+            shutil.copy2(s, d)
+
+
+class VersionString:
+    def __init__(self, version):
+        self.version = version
+
+    def __str__(self):
+        return self.version
+
+    def __repr__(self):
+        return "VersionString:" + self.version
+
+    def _preprocess(self, v, separator, ignorecase):
+        if ignorecase:
+            v = v.lower()
+        return [
+            int(x)
+            if x.isdigit()
+            else [int(y) if y.isdigit() else y for y in re.findall("\d+|[a-zA-Z]+", x)]
+            for x in re.split(separator, v)
+        ]
+
+    def compare(self, version2, separator=". |-", ignorecase=True):
+        # return 1 if self.version > version2
+        # return 0 if self.version == version2
+        # return -1 if self.version < version2
+        # return False if not comparable
+        if "rc" in self.version and not "rc" in version2:
+            a = self._preprocess(
+                self.version.split("rc")[0].strip("-"), separator, ignorecase
+            )
+            b = b = self._preprocess(version2, separator, ignorecase)
+            if ((a > b) - (a < b)) == 0:
+                return -1
+            else:
+                return (a > b) - (a < b)
+        else:
+            a = self._preprocess(self.version, separator, ignorecase)
+            b = self._preprocess(version2, separator, ignorecase)
+            try:
+                return (a > b) - (a < b)
+            except:
+                return False
+
+
+SUPPORTED_PLATFORM_MACHINE = {
+    "Linux": ["x86_64", "amd64", "arm64", "armv8", "aarch64"],
+    "Darwin": ["x86_64", "arm64", "arm"],
+}
+
+SUPPORTED_MIN_VERSION = {
+    "Linux" + "x86_64": VersionString("0.9.0"),
+    "Linux" + "amd64": VersionString("0.9.0"),
+    "Linux" + "arm64": VersionString("0.9.0"),
+    "Linux" + "armv8": VersionString("0.9.0"),
+    "Linux" + "aarch64": VersionString("0.9.0"),
+    "Darwin" + "x86_64": VersionString("0.9.0"),
+    "Darwin" + "arm64": VersionString("0.9.0"),
+    "Darwin" + "arm": VersionString("0.9.0"),
+}
+
+WASMEDGE = "WasmEdge"
+WASMEDGE_UNINSTALLER = "WasmEdge_Uninstaller"
+TENSORFLOW = "tensorflow"
+TENSORFLOW_LITE = "tensorflow_lite"
+TENSORFLOW_DEPS = "tensorflow_deps"
+TENSORFLOW_LITE_DEPS = "tensorflow_lite_deps"
+TENSORFLOW_TOOLS = "tensorflow_tools"
+IMAGE = "image"
+EXTENSIONS = [TENSORFLOW, IMAGE]
+
+SUPPORTED_EXTENSIONS = {
+    "Linux" + "x86_64": EXTENSIONS,
+    "Linux" + "amd64": EXTENSIONS,
+    "Linux" + "arm64": EXTENSIONS,
+    "Linux" + "armv8": EXTENSIONS,
+    "Linux" + "aarch64": EXTENSIONS,
+    "Darwin" + "x86_64": EXTENSIONS,
+    "Darwin" + "arm64": [],
+    "Darwin" + "arm": [],
+}
+
+SUPPORTED_EXTENSIONS_VERSION = {
+    "Linux" + "x86_64" + TENSORFLOW: VersionString("0.9.0"),
+    "Linux" + "x86_64" + IMAGE: VersionString("0.9.0"),
+    "Linux" + "amd64" + TENSORFLOW: VersionString("0.9.0"),
+    "Linux" + "amd64" + IMAGE: VersionString("0.9.0"),
+    "Linux" + "arm64" + TENSORFLOW: VersionString("0.9.0"),
+    "Linux" + "arm64" + IMAGE: VersionString("0.9.0"),
+    "Linux" + "armv8" + TENSORFLOW: VersionString("0.9.0"),
+    "Linux" + "armv8" + IMAGE: VersionString("0.9.0"),
+    "Linux" + "aarch64" + TENSORFLOW: VersionString("0.9.1-beta.1"),
+    "Linux" + "aarch64" + IMAGE: VersionString("0.9.1-beta.1"),
+    "Darwin" + "x86_64" + TENSORFLOW: VersionString("0.10.0-alpha.1"),
+    "Darwin" + "x86_64" + IMAGE: VersionString("0.10.0-alpha.1"),
+    "Darwin" + "arm64" + TENSORFLOW: VersionString("0.10.0-alpha.1"),
+    "Darwin" + "arm" + TENSORFLOW: VersionString("0.10.0-alpha.1"),
+}
+
+WASI_NN_OPENVINO = "wasi_nn-openvino"
+WASI_CRYPTO = "wasi_crypto"
+WASI_NN_PYTORCH = "wasi_nn-pytorch"
+WASI_NN_TENSORFLOW_LITE = "wasi_nn-tensorflowlite"
+WASMEDGE_HTTPSREQ = "wasmedge_httpsreq"
+PLUGINS_AVAILABLE = [
+    WASI_NN_OPENVINO,
+    WASI_CRYPTO,
+    WASI_NN_PYTORCH,
+    WASI_NN_TENSORFLOW_LITE,
+    WASMEDGE_HTTPSREQ,
+]
+
+SUPPORTTED_PLUGINS = {
+    "ubuntu20.04" + "x86_64" + WASI_CRYPTO: VersionString("0.10.1-rc.1"),
+    "manylinux2014" + "x86_64" + WASI_CRYPTO: VersionString("0.10.1-rc.1"),
+    "manylinux2014" + "aarch64" + WASI_CRYPTO: VersionString("0.10.1-rc.1"),
+    "manylinux2014" + "arm64" + WASI_CRYPTO: VersionString("0.10.1-rc.1"),
+    "ubuntu20.04" + "x86_64" + WASI_NN_OPENVINO: VersionString("0.10.1-alpha.1"),
+    "ubuntu20.04" + "x86_64" + WASI_NN_PYTORCH: VersionString("0.11.1-alpha.1"),
+    "manylinux2014" + "x86_64" + WASI_NN_PYTORCH: VersionString("0.11.2-alpha.1"),
+    "manylinux2014"
+    + "x86_64"
+    + WASI_NN_TENSORFLOW_LITE: VersionString("0.11.2-alpha.1"),
+    "manylinux2014"
+    + "aarch64"
+    + WASI_NN_TENSORFLOW_LITE: VersionString("0.11.2-alpha.1"),
+    "ubuntu20.04" + "x86_64" + WASI_NN_TENSORFLOW_LITE: VersionString("0.11.2-rc.1"),
+    "ubuntu20.04" + "x86_64" + WASMEDGE_HTTPSREQ: VersionString("0.11.1"),
+    "manylinux2014" + "x86_64" + WASMEDGE_HTTPSREQ: VersionString("0.11.1"),
+    "manylinux2014" + "aarch64" + WASMEDGE_HTTPSREQ: VersionString("0.11.1"),
+}
+
+HOME = expanduser("~")
+PATH = join(HOME, ".wasmedge")
+SHELL = getenv("SHELL", "bash").split("/")[-1]
+TEMP_PATH = join(tempfile.gettempdir(), "wasmedge." + str(getpid()))
+CONST_shell_config = None
+CONST_shell_profile = None
+CONST_env = None
+CONST_urls = None
+CONST_release_pkg = None
+CONST_ipkg = None
+CONST_lib_ext = None
+CONST_env_path = None
+CONST_lib_dir = "lib"
+
+try:
+    mkdir(TEMP_PATH)
+except:
+    pass
+
+
+def set_env(args, compat):
+    global CONST_env, CONST_env_path, CONST_lib_dir
+
+    CONST_env = """#!/bin/sh
+# wasmedge shell setup
+# affix colons on either side of $PATH to simplify matching
+case :"${1}": in
+    *:"{0}/bin":*)
+        ;;
+    *)
+        # Prepending path in case a system-installed wasmedge needs to be overridden
+        if [ -n "${1}" ]; then
+            export PATH="{0}/bin":$PATH
+        else
+            export PATH="{0}/bin"
+        fi
+        ;;
+esac
+case :"${2}": in
+    *:"{0}/{6}":*)
+        ;;
+    *)
+        # Prepending path in case a system-installed wasmedge libs needs to be overridden
+        if [ -n "${2}" ]; then
+            export {2}="{0}/{6}":${2}
+        else
+            export {2}="{0}/{6}"
+        fi
+        ;;
+esac
+case :"${3}": in
+    *:"{0}/{6}":*)
+        ;;
+    *)
+        if [ -n "${3}" ]; then
+            export LIBRARY_PATH="{0}/{6}":$LIBRARY_PATH
+        else
+            export LIBRARY_PATH="{0}/{6}"
+        fi
+        ;;
+esac
+case :"${4}": in
+    *:"{0}/include":*)
+        ;;
+    *)
+        if [ -n "${4}" ]; then
+            export C_INCLUDE_PATH="{0}/include":$C_INCLUDE_PATH
+        else
+            export C_INCLUDE_PATH="{0}/include"
+        fi
+        ;;
+esac
+case :"${5}": in
+    *:"{0}/include":*)
+        ;;
+    *)
+        if [ -n "${5}" ]; then
+            export CPLUS_INCLUDE_PATH="{0}/include":$CPLUS_INCLUDE_PATH
+        else
+            export CPLUS_INCLUDE_PATH="{0}/include"
+        fi
+        ;;
+esac
+if [ -z ${{WASMEDGE_LIB_DIR+x}} ]; then
+    export WASMEDGE_LIB_DIR="{0}/{6}"
+fi
+# Please do not edit comments below this for uninstallation purpose
+""".format(
+        args.path,
+        "PATH",
+        compat.ld_library_path,
+        "LIBRARY_PATH",
+        "C_INCLUDE_PATH",
+        "CPLUS_INCLUDE_PATH",
+        CONST_lib_dir,
+    )
+
+    try:
+        mkdir(args.path)
+        if is_default_path(args):
+            mkdir(join(args.path, "plugin"))
+    except:
+        pass
+    CONST_env_path = join(args.path, "env")
+    mode = "w+" if not exists(CONST_env_path) else "w"
+    with opened_w_error(CONST_env_path, mode) as env:
+        if env is not None:
+            env.write(CONST_env)
+        else:
+            logging.error("Not able to write to env file")
+
+
+def shell_configure(args, compat):
+    global CONST_shell_profile, CONST_shell_config
+
+    source_string = '\n. "{0}"\n'.format(join(args.path, "env"))
+
+    if ("bash" in SHELL) or ("zsh" in SHELL):
+        CONST_shell_config = join(HOME, "." + SHELL + "rc")
+
+        if "zsh" in SHELL:
+            CONST_shell_profile = join(HOME, "." + "zprofile")
+        else:
+            CONST_shell_profile = join(HOME, "." + SHELL + "_profile")
+
+        # On Darwin: Create shell config only if shell_profile does not exist
+        # On Linux: Create shell config anyway
+        if not exists(CONST_shell_config) and compat.platform != "Darwin":
+            open(CONST_shell_config, "a").close()
+
+        write_shell = False
+        if compat.platform != "Darwin":
+            with opened_w_error(CONST_shell_config, "r") as shell_config:
+                if shell_config is not None:
+                    if source_string not in shell_config.read():
+                        write_shell = True
+
+        # On Darwin: Append to shell config only if shell_profile does not exist
+        # On Linux: Append to shell config anyway
+        if write_shell and compat.platform != "Darwin":
+            with opened_w_error(CONST_shell_config, "a") as shell_config:
+                if shell_config is not None:
+                    shell_config.write(source_string)
+            write_shell = False
+
+        if exists(CONST_shell_profile):
+            with opened_w_error(CONST_shell_profile, "r") as shell_profile:
+                if shell_profile is not None:
+                    if source_string not in shell_profile.read():
+                        write_shell = True
+            if write_shell:
+                with opened_w_error(CONST_shell_profile, "a") as shell_profile:
+                    if shell_profile is not None:
+                        shell_profile.write(source_string)
+                write_shell = False
+    else:
+        logging.error("Unknown shell found")
+        return -1
+
+    print("shell configuration updated")
+    return 0
+
+
+def fix_gnu_sparse(args):
+    # Fix GNUSparseFile.0 folder in macOS if exists
+    global CONST_lib_ext, CONST_lib_dir
+
+    for dir in listdir(args.path):
+        if not isdir(join(args.path, dir)):
+            continue
+        if "GNUSparseFile" in dir:
+            for file in listdir(join(args.path, dir)):
+                if file.endswith(CONST_lib_ext):
+                    if isdir(join(args.path, CONST_lib_dir)):
+                        shutil.move(
+                            join(args.path, dir, file), join(args.path, CONST_lib_dir)
+                        )
+                    else:
+                        logging.error(
+                            "%s directory not found", join(args.path, CONST_lib_dir)
+                        )
+                        try:
+                            mkdir(join(args.path, CONST_lib_dir))
+                            shutil.move(
+                                join(args.path, dir, file),
+                                join(args.path, CONST_lib_dir),
+                            )
+                        except:
+                            pass
+                elif (
+                    file.endswith(".h")
+                    or file.endswith(".hpp")
+                    or file.endswith(".inc")
+                ):
+                    shutil.move(join(args.path, dir, file), join(args.path, "include"))
+                else:
+                    shutil.move(join(args.path, dir, file), join(args.path, "bin"))
+        for sub_dir in listdir(join(args.path, dir)):
+            if not isdir(join(args.path, dir, sub_dir)):
+                continue
+            if "GNUSparseFile" in sub_dir:
+                for file in listdir(join(args.path, dir, sub_dir)):
+                    shutil.move(
+                        join(args.path, dir, sub_dir, file), join(args.path, dir)
+                    )
+                if len(listdir(join(args.path, dir, sub_dir))) == 0:
+                    shutil.rmtree(join(args.path, dir, sub_dir))
+
+
+def ldconfig(args, compat):
+    if geteuid() == 0:
+        # Only run ldconfig or update_dyld_shared_cache when user is root/sudoer
+        if compat.platform == "Linux":
+            cmd = "ldconfig {0}".format(join(args.path, CONST_lib_dir))
+            output = run_shell_command(cmd)
+            logging.debug("%s: %s", cmd, output)
+        elif compat.platform == "Darwin":
+            cmd = "update_dyld_shared_cache {0}".format(join(args.path, CONST_lib_dir))
+            output = run_shell_command(cmd)
+            logging.debug("%s: %s", cmd, output)
+        else:
+            logging.warning("Help adding ldconfig for your platform")
+    else:
+        logging.debug("Not root or sudoer, skip ldconfig")
+
+
+def is_default_path(args):
+    global PATH
+    return args.path == abspath(PATH) or args.path[:4] != "/usr"
+
+
+def install_image_extension(args, compat):
+    global CONST_release_pkg, CONST_lib_dir
+
+    if not get_remote_version_availability(
+        "second-state/WasmEdge-image", args.image_version
+    ):
+        logging.error(
+            "Image extension version incorrect: {0}".format(args.image_version)
+        )
+        return -1
+    if compat.prefix() + IMAGE not in SUPPORTED_EXTENSIONS_VERSION:
+        logging.error("Image extensions not compatible: {0}".format(compat.prefix()))
+        return -1
+    elif (
+        SUPPORTED_EXTENSIONS_VERSION[compat.prefix() + IMAGE].compare(
+            args.image_version
+        )
+        > 0
+    ):
+        logging.error(
+            "Min image extensions version: {0}".format(
+                SUPPORTED_EXTENSIONS_VERSION[compat.prefix() + IMAGE],
+            )
+        )
+        return -1
+
+    print("Downloading image extension")
+
+    local_release_package = CONST_release_pkg
+
+    # From WasmEdge 0.11.1, we have the Ubuntu release.
+    # Installation of ubuntu version extensions when the ubuntu version of WasmEdge selected.
+    if VersionString(args.image_version).compare("0.11.1") >= 0:
+        local_release_package = compat.release_package_wasmedge
+        logging.debug("Downloading dist package: {0}".format(local_release_package))
+
+    image_pkg = "WasmEdge-image-" + args.image_version + "-" + local_release_package
+
+    download_url(CONST_urls[IMAGE], join(TEMP_PATH, image_pkg), show_progress)
+
+    # Extract archive
+    extract_archive(
+        join(TEMP_PATH, image_pkg),
+        args.path,
+        join(TEMP_PATH, "WasmEdge-image"),
+        env_file_path=CONST_env_path,
+        remove_finished=True,
+    )
+
+    wasmedge_image_temp = join(TEMP_PATH, "WasmEdge-image")
+    for dir in listdir(wasmedge_image_temp):
+        wasmedge_image_temp_dir = join(wasmedge_image_temp, dir)
+        for file in listdir(wasmedge_image_temp_dir):
+            if isdir(join(wasmedge_image_temp_dir, file)) and "wasmedge" == file:
+                copytree(
+                    join(wasmedge_image_temp_dir, file),
+                    join(args.path, "include", "wasmedge"),
+                )
+            elif CONST_lib_ext in file:
+                if isdir(join(args.path, CONST_lib_dir)):
+                    shutil.move(
+                        join(wasmedge_image_temp_dir, file),
+                        join(args.path, CONST_lib_dir, file),
+                    )
+                else:
+                    logging.error(
+                        "%s directory not found", join(args.path, CONST_lib_dir)
+                    )
+                    try:
+                        mkdir(join(args.path, CONST_lib_dir))
+                        shutil.move(
+                            join(wasmedge_image_temp_dir, file),
+                            join(args.path, "lib", file),
+                        )
+                    except:
+                        pass
+            elif isdir(join(wasmedge_image_temp_dir, file)):
+                copytree(
+                    join(wasmedge_image_temp_dir, file),
+                    join(args.path, file),
+                )
+            else:
+                shutil.move(
+                    join(wasmedge_image_temp_dir, file),
+                    join(args.path, "bin", file),
+                )
+
+    fix_gnu_sparse(args)
+
+    return 0
+
+
+def install_tensorflow_extension(args, compat):
+    global CONST_release_pkg, CONST_lib_ext, CONST_lib_dir, CONST_env_path
+
+    if not get_remote_version_availability(
+        "second-state/WasmEdge-tensorflow", args.tf_version
+    ):
+        logging.error(
+            "Tensorflow extension version incorrect: {0}".format(args.tf_version)
+        )
+        return -1
+    elif not get_remote_version_availability(
+        "second-state/WasmEdge-tensorflow-deps", args.tf_deps_version
+    ):
+        logging.error(
+            "Tensorflow Deps extension version incorrect: {0}".format(
+                args.tf_deps_version
+            )
+        )
+        return -1
+    elif not get_remote_version_availability(
+        "second-state/WasmEdge-tensorflow", args.tf_tools_version
+    ):
+        logging.error(
+            "Tensorflow Tools version incorrect: {0}".format(args.tf_tools_version)
+        )
+        return -1
+
+    if compat.prefix() + TENSORFLOW not in SUPPORTED_EXTENSIONS_VERSION:
+        logging.error(
+            "Tensorflow extensions not compatible: {0}".format(compat.prefix())
+        )
+        return -1
+    elif (
+        SUPPORTED_EXTENSIONS_VERSION[compat.prefix() + TENSORFLOW].compare(
+            args.tf_version
+        )
+        > 0
+    ):
+        logging.error(
+            "Min tensorflow extensions version: {0}".format(
+                SUPPORTED_EXTENSIONS_VERSION[compat.prefix() + TENSORFLOW],
+            )
+        )
+        return -1
+
+    download_tf = True
+    download_tf_lite = True
+
+    if compat.machine == "aarch64":
+        download_tf = False
+
+    local_release_package = CONST_release_pkg
+
+    # From WasmEdge 0.11.1, we have the Ubuntu release.
+    # Installation of ubuntu version extensions when the ubuntu version of WasmEdge selected.
+    if VersionString(args.tf_version).compare("0.11.1") >= 0:
+        local_release_package = compat.release_package_wasmedge
+        logging.debug("Downloading dist package: {0}".format(local_release_package))
+
+    if download_tf:
+        tf_pkg = "WasmEdge-tensorflow-" + args.tf_version + "-" + local_release_package
+        tf_deps_pkg = (
+            "WasmEdge-tensorflow-deps-TF-"
+            + args.tf_deps_version
+            + "-"
+            + CONST_release_pkg
+        )
+
+        print("Downloading tensorflow extension")
+        download_url(CONST_urls[TENSORFLOW], join(TEMP_PATH, tf_pkg), show_progress)
+
+        print("Downloading tensorflow-deps")
+        download_url(
+            CONST_urls[TENSORFLOW_DEPS], join(TEMP_PATH, tf_deps_pkg), show_progress
+        )
+
+        # Extract archive
+        extract_archive(
+            join(TEMP_PATH, tf_pkg),
+            args.path,
+            join(TEMP_PATH, "WasmEdge-tensorflow"),
+            env_file_path=CONST_env_path,
+            remove_finished=True,
+        )
+
+        # Extract archive
+        extract_archive(
+            join(TEMP_PATH, tf_deps_pkg),
+            join(args.path, CONST_lib_dir),
+            join(TEMP_PATH, "WasmEdge-tensorflow-deps", CONST_lib_dir),
+            env_file_path=CONST_env_path,
+            remove_finished=True,
+        )
+
+        copytree(join(TEMP_PATH, "WasmEdge-tensorflow"), args.path)
+        copytree(join(TEMP_PATH, "WasmEdge-tensorflow-deps"), args.path)
+
+    if download_tf_lite:
+        tf_lite_pkg = (
+            "WasmEdge-tensorflowlite-" + args.tf_version + "-" + local_release_package
+        )
+        tf_deps_lite_pkg = (
+            "WasmEdge-tensorflow-deps-TFLite-"
+            + args.tf_deps_version
+            + "-"
+            + CONST_release_pkg
+        )
+
+        print("Downloading tensorflow-lite extension")
+        download_url(
+            CONST_urls[TENSORFLOW_LITE], join(TEMP_PATH, tf_lite_pkg), show_progress
+        )
+
+        print("Downloading tensorflow-lite-deps")
+        download_url(
+            CONST_urls[TENSORFLOW_LITE_DEPS],
+            join(TEMP_PATH, tf_deps_lite_pkg),
+            show_progress,
+        )
+
+        # Extract archive
+        extract_archive(
+            join(TEMP_PATH, tf_lite_pkg),
+            args.path,
+            join(TEMP_PATH, "WasmEdge-tensorflow-lite"),
+            env_file_path=CONST_env_path,
+            remove_finished=True,
+        )
+
+        # Extract archive
+        extract_archive(
+            join(TEMP_PATH, tf_deps_lite_pkg),
+            join(args.path, CONST_lib_dir),
+            join(TEMP_PATH, "WasmEdge-tensorflow-lite-deps", CONST_lib_dir),
+            env_file_path=CONST_env_path,
+            remove_finished=True,
+        )
+
+        copytree(join(TEMP_PATH, "WasmEdge-tensorflow-lite"), args.path)
+        copytree(join(TEMP_PATH, "WasmEdge-tensorflow-lite-deps"), args.path)
+
+    tf_tools_pkg = (
+        "WasmEdge-tensorflow-tools-" + args.tf_tools_version + "-" + CONST_release_pkg
+    )
+
+    print("Downloading tensorflow-tools extension")
+    download_url(
+        CONST_urls[TENSORFLOW_TOOLS], join(TEMP_PATH, tf_tools_pkg), show_progress
+    )
+
+    # Extract archive
+    extract_archive(
+        join(TEMP_PATH, tf_tools_pkg),
+        join(args.path, "bin"),
+        join(TEMP_PATH, "WasmEdge-tensorflow-tools", "bin"),
+        env_file_path=CONST_env_path,
+        remove_finished=True,
+    )
+
+    copytree(join(TEMP_PATH, "WasmEdge-tensorflow-tools"), args.path)
+
+    fix_gnu_sparse(args)
+
+    all_files = run_shell_command("ls -R {0}".format(TEMP_PATH))
+
+    if not isdir(join(args.path, CONST_lib_dir)):
+        logging.error("Strange: No %s directory found", CONST_lib_dir)
+
+    for file in listdir(join(args.path, CONST_lib_dir)):
+        if CONST_lib_ext not in file:
+            # ignore files that are not libraries
+            continue
+        if file not in all_files:
+            # ignore files that are not downloaded by this script
+            continue
+        if "tensorflow" not in file:
+            continue
+        # check if it contains any digits
+        if not any(i.isdigit() for i in file):
+            continue
+        if compat.platform == "Linux":
+            name, version = file.split(CONST_lib_ext, 1)
+            if version[0] == ".":
+                version = version[1:]
+            if version != "" and version.count(".") >= 2:
+                no_v_name = name + CONST_lib_ext
+                single_v_name = name + CONST_lib_ext + "." + version.split(".")[0]
+                dual_v_name = (
+                    name
+                    + CONST_lib_ext
+                    + "."
+                    + version.split(".")[0]
+                    + "."
+                    + version.split(".")[1]
+                )
+                file_path = join(args.path, CONST_lib_dir, file)
+                single_v_file_path = join(args.path, CONST_lib_dir, single_v_name)
+                dual_v_file_path = join(args.path, CONST_lib_dir, dual_v_name)
+                no_v_file_path = join(args.path, CONST_lib_dir, no_v_name)
+                try:
+                    symlink(file_path, single_v_file_path)
+                    symlink(file_path, dual_v_file_path)
+                    symlink(file_path, no_v_file_path)
+                except Exception as e:
+                    logging.debug(e)
+            else:
+                continue
+        elif compat.platform == "Darwin":
+            name, version = file.split(CONST_lib_ext, 1)[0].split(".", 1)
+            if version != "" and version.count(".") >= 2:
+                no_v_name = name + CONST_lib_ext
+                single_v_name = name + "." + version.split(".")[0] + CONST_lib_ext
+                dual_v_name = (
+                    name
+                    + "."
+                    + version.split(".")[0]
+                    + "."
+                    + version.split(".")[1]
+                    + CONST_lib_ext
+                )
+                file_path = join(args.path, CONST_lib_dir, file)
+                single_v_file_path = join(args.path, CONST_lib_dir, single_v_name)
+                dual_v_file_path = join(args.path, CONST_lib_dir, dual_v_name)
+                no_v_file_path = join(args.path, CONST_lib_dir, no_v_name)
+                try:
+                    symlink(file_path, single_v_file_path)
+                    symlink(file_path, dual_v_file_path)
+                    symlink(file_path, no_v_file_path)
+                except Exception as e:
+                    logging.debug(e)
+            else:
+                continue
+        else:
+            reraise(Exception("Not implemented for {0}".format(compat.platform)))
+        with opened_w_error(CONST_env_path, "a") as env_file:
+            if env_file is not None:
+                env_file.write("#" + single_v_file_path + "\n")
+                logging.debug("Appending:%s", single_v_file_path)
+                env_file.write("#" + dual_v_file_path + "\n")
+                logging.debug("Appending:%s", dual_v_file_path)
+                env_file.write("#" + no_v_file_path + "\n")
+                logging.debug("Appending:%s", no_v_file_path)
+            else:
+                logging.error("Not able to append installed files to env file")
+
+    for main_dir in ["WasmEdge-tensorflow", "WasmEdge-tensorflow-lite"]:
+        if not isdir(join(TEMP_PATH, main_dir)):
+            continue
+        for directory_file in listdir(join(TEMP_PATH, main_dir)):
+            if isdir(directory_file):
+                wasmedge_tf_folder = join(TEMP_PATH, main_dir, directory_file)
+                for _file in listdir(wasmedge_tf_folder):
+                    if (
+                        _file == "wasmedge"
+                        and isdir(join(wasmedge_tf_folder, _file))
+                        and is_default_path(args)
+                    ):
+                        copytree(
+                            join(wasmedge_tf_folder, _file),
+                            join(args.path, "include", "wasmedge"),
+                        )
+                    elif CONST_lib_ext in _file:
+                        if isdir(join(args.path, CONST_lib_dir)):
+                            shutil.move(
+                                join(wasmedge_tf_folder, _file),
+                                join(args.path, CONST_lib_dir, _file),
+                            )
+                        else:
+                            logging.error(
+                                "%s is not a directory", join(args.path, CONST_lib_dir)
+                            )
+                            try:
+                                mkdir(join(args.path, CONST_lib_dir))
+                                shutil.move(
+                                    join(wasmedge_tf_folder, _file),
+                                    join(args.path, CONST_lib_dir, _file),
+                                )
+                            except:
+                                pass
+
+                    elif isdir(join(wasmedge_tf_folder, _file)):
+                        copytree(
+                            join(wasmedge_tf_folder, _file),
+                            join(args.path, _file),
+                        )
+                    else:
+                        shutil.move(
+                            join(wasmedge_tf_folder, _file),
+                            join(args.path, "bin", _file),
+                        )
+
+    if download_tf:
+        # Check if wasmedge binary works
+        wasmedge_tf_output = run_shell_command(
+            ". {0}/env &&{0}/bin/wasmedge-tensorflow --version".format(args.path)
+        )
+
+        if args.tf_version in wasmedge_tf_output:
+            print("WasmEdge Successfully installed")
+        else:
+            logging.critical(
+                "WasmEdge Tensorflow installation incorrect: {0}".format(
+                    wasmedge_tf_output
+                )
+            )
+
+    if download_tf_lite:
+        # Check if wasmedge binary works
+        wasmedge_tf_lite_output = run_shell_command(
+            ". {0}/env && {0}/bin/wasmedge-tensorflow-lite --version".format(args.path)
+        )
+
+        if args.tf_version in wasmedge_tf_lite_output:
+            print("WasmEdge Tensorflow Lite Successfully installed")
+        else:
+            logging.critical(
+                "WasmEdge Tensorflow installation incorrect: {0}".format(
+                    wasmedge_tf_lite_output
+                )
+            )
+
+    return 0
+
+
+def install_plugins(args, compat):
+    global CONST_lib_dir
+    url_root = "https://github.com/WasmEdge/WasmEdge/releases/download/"
+    url_root += "$VERSION$/WasmEdge-plugin-$PLUGIN_NAME$-$VERSION$-$DIST$_$ARCH$.tar.gz"
+
+    if len(args.plugins) >= 1:
+        for plugin_name in args.plugins:
+            plugin_version_supplied = None
+            if plugin_name.find(":") != -1:
+                plugin_name, plugin_version_supplied = plugin_name.split(":")
+
+            if plugin_name not in PLUGINS_AVAILABLE:
+                logging.error(
+                    "%s plugin not found, available names - %s",
+                    plugin_name,
+                    PLUGINS_AVAILABLE,
+                )
+                continue
+
+            if compat.dist + compat.machine + plugin_name not in SUPPORTTED_PLUGINS:
+                logging.error(
+                    "Plugin not compatible: %s",
+                    compat.dist + compat.machine + plugin_name,
+                )
+                logging.debug("Supported: %s", SUPPORTTED_PLUGINS)
+                continue
+            else:
+                if plugin_version_supplied is None:
+                    plugin_version_supplied = args.version
+                elif (
+                    SUPPORTTED_PLUGINS[
+                        compat.dist + compat.machine + plugin_name
+                    ].compare(plugin_version_supplied)
+                    > 0
+                ):
+                    logging.error(
+                        "Plugin not compatible: %s %s",
+                        plugin_name,
+                        plugin_version_supplied,
+                    )
+                    continue
+
+                plugin_url = (
+                    url_root.replace("$PLUGIN_NAME$", plugin_name)
+                    .replace("$VERSION$", plugin_version_supplied)
+                    .replace("$DIST$", compat.dist)
+                    .replace("$ARCH$", compat.machine)
+                )
+                logging.debug("Plugin URL: %s", plugin_url)
+
+                print("Downloading Plugin: " + plugin_name)
+                download_url(
+                    plugin_url,
+                    join(TEMP_PATH, "Plugin" + plugin_name) + ".tar.gz",
+                    show_progress,
+                )
+                extract_archive(
+                    join(TEMP_PATH, "Plugin" + plugin_name + ".tar.gz"),
+                    join(args.path),
+                    join(TEMP_PATH, "Plugins"),
+                    env_file_path=CONST_env_path,
+                    remove_finished=True,
+                )
+
+        if isdir(join(TEMP_PATH, "Plugins")):
+            if is_default_path(args):
+                copytree(join(TEMP_PATH, "Plugins"), join(args.path, "plugin"))
+            else:
+                copytree(
+                    join(TEMP_PATH, "Plugins"),
+                    join(args.path, CONST_lib_dir, "wasmedge"),
+                )
+
+
+def set_consts(args, compat):
+    global CONST_release_pkg, CONST_ipkg, CONST_lib_ext, CONST_urls, CONST_lib_dir, CONST_env_path
+    CONST_release_pkg = compat.release_package
+    CONST_ipkg = compat.install_package_name
+    CONST_lib_ext = compat.lib_extension
+
+    local_release_package_tf = CONST_release_pkg
+
+    # From WasmEdge 0.11.1, we have the Ubuntu release.
+    # Installation of ubuntu version extensions when the ubuntu version of WasmEdge selected.
+    if VersionString(args.tf_version).compare("0.11.1") >= 0:
+        local_release_package_tf = compat.release_package_wasmedge
+        logging.debug("Tensorflow release pkg: {0}".format(local_release_package_tf))
+
+    local_release_package_im = CONST_release_pkg
+
+    # From WasmEdge 0.11.1, we have the Ubuntu release.
+    # Installation of ubuntu version extensions when the ubuntu version of WasmEdge selected.
+    if VersionString(args.image_version).compare("0.11.1") >= 0:
+        local_release_package_im = compat.release_package_wasmedge
+        logging.debug("Image release pkg: {0}".format(local_release_package_im))
+
+    CONST_urls = {
+        WASMEDGE: "https://github.com/WasmEdge/WasmEdge/releases/download/{0}/WasmEdge-{0}-{1}".format(
+            args.version, compat.release_package_wasmedge
+        ),
+        WASMEDGE_UNINSTALLER: "https://raw.githubusercontent.com/WasmEdge/WasmEdge/{0}/utils/uninstall.sh".format(
+            args.uninstall_script_tag
+        ),
+        IMAGE: "https://github.com/second-state/WasmEdge-image/releases/download/{0}/WasmEdge-image-{0}-{1}".format(
+            args.image_version, local_release_package_im
+        ),
+        TENSORFLOW_DEPS: "https://github.com/second-state/WasmEdge-tensorflow-deps/releases/download/{0}/WasmEdge-tensorflow-deps-TF-{0}-{1}".format(
+            args.tf_deps_version, CONST_release_pkg
+        ),
+        TENSORFLOW_LITE_DEPS: "https://github.com/second-state/WasmEdge-tensorflow-deps/releases/download/{0}/WasmEdge-tensorflow-deps-TFLite-{0}-{1}".format(
+            args.tf_deps_version, CONST_release_pkg
+        ),
+        TENSORFLOW: "https://github.com/second-state/WasmEdge-tensorflow/releases/download/{0}/WasmEdge-tensorflow-{0}-{1}".format(
+            args.tf_version, local_release_package_tf
+        ),
+        TENSORFLOW_LITE: "https://github.com/second-state/WasmEdge-tensorflow/releases/download/{0}/WasmEdge-tensorflowlite-{0}-{1}".format(
+            args.tf_version, local_release_package_tf
+        ),
+        TENSORFLOW_TOOLS: "https://github.com/second-state/WasmEdge-tensorflow-tools/releases/download/{0}/WasmEdge-tensorflow-tools-{0}-{1}".format(
+            args.tf_tools_version, CONST_release_pkg
+        ),
+    }
+
+
+def run_shell_command(cmd):
+    try:
+        output = subprocess.check_output([cmd], shell=True)
+        return output.decode("utf8").strip()
+    except subprocess.CalledProcessError as e:
+        if "Cannot detect installation path" in str(e.output):
+            logging.warning("Uninstaller did not find previous installation")
+        else:
+            print("Exception on process, rc=", e.returncode, "output=", e.output, e.cmd)
+
+    return ""
+
+
+def get_latest_github_release(repo):
+    return run_shell_command(
+        """git ls-remote --refs --tags "https://github.com/{0}.git" |
+        cut -d '/' -f 3 |
+        awk {1} | sort --version-sort | sed 's/_$//' |
+        grep -e '^[0-9]\+.[0-9]\+.[0-9]\+$' |
+        tail -1""".format(
+            repo,
+            "'{ if ($1 ~ /-/) print; else print $0\"_\" ;}'",
+        )
+    )
+
+
+def get_remote_version_availability(repo, version):
+    output = run_shell_command(
+        """git ls-remote --refs --tags "https://github.com/{0}.git" |
+        cut -d '/' -f 3 |
+        awk {1} | sort --version-sort | sed 's/_$//'""".format(
+            repo,
+            "'{ if ($1 ~ /-/) print; else print $0\"_\" ;}'",
+        )
+    )
+    if version in output:
+        return True
+    return False
+
+
+class Compat:
+    def __init__(
+        self,
+        platform_=platform.system(),
+        machine=platform.machine(),
+        dist_=None,
+        version=None,
+        extensions=None,
+    ):
+        self.platform = platform_  # Linux, Darwin
+        self.machine = machine  # x86_64, arm
+        self.version = VersionString(version)
+        self.extensions = extensions
+        self.release_package = None
+        self.install_package_name = None
+        self.lib_extension = None
+        self.ld_library_path = None
+        self.dist = dist_
+        self.release_package_wasmedge = None
+
+        if self.platform == "Linux":
+            self.install_package_name = "WasmEdge-{0}-Linux".format(self.version)
+            self.lib_extension = ".so"
+            self.ld_library_path = "LD_LIBRARY_PATH"
+
+            if self.machine in ["arm64", "armv8", "aarch64"]:
+                self.release_package = "manylinux2014_aarch64.tar.gz"
+            elif self.machine in ["x86_64", "amd64"]:
+                self.release_package = "manylinux2014_x86_64.tar.gz"
+            else:
+                reraise(Exception("Unsupported arch: {0}".format(self.machine)))
+
+            self.release_package_wasmedge = self.release_package
+
+            if self.dist is None:
+                if sys.version_info[0] == 2:
+                    if (
+                        "Ubuntu" in platform.dist() and "20.04" in platform.dist()
+                    ) or "Ubuntu 20.04" in run_shell_command(
+                        "lsb_release -d | awk -F'\t' '{print $2}'"
+                    ):
+                        self.dist = "ubuntu20.04"
+                    else:
+                        self.dist = "manylinux2014"
+                elif sys.version_info[0] == 3:
+                    __lsb_rel = run_shell_command(
+                        "cat /etc/lsb-release | grep RELEASE"
+                    )[-5:]
+                    if "20.04" == __lsb_rel or "Ubuntu 20.04" in run_shell_command(
+                        "lsb_release -d | awk -F'\t' '{print $2}'"
+                    ):
+                        self.dist = "ubuntu20.04"
+                    else:
+                        self.dist = "manylinux2014"
+
+            # Below version 0.11.1 different distributions for wasmedge binary do not exist
+            if self.version.compare("0.11.1") != -1:
+                if self.machine in ["arm64", "armv8", "aarch64"]:
+                    self.release_package_wasmedge = self.dist + "_aarch64.tar.gz"
+                elif self.machine in ["x86_64", "amd64"]:
+                    self.release_package_wasmedge = self.dist + "_x86_64.tar.gz"
+                else:
+                    reraise(Exception("Unsupported arch: {0}".format(self.machine)))
+
+        elif self.platform == "Darwin":
+            self.ld_library_path = "DYLD_LIBRARY_PATH"
+            self.install_package_name = "WasmEdge-{0}-Darwin".format(self.version)
+            self.release_package = "darwin_{0}.tar.gz".format(self.machine)
+            self.release_package_wasmedge = self.release_package
+            self.lib_extension = ".dylib"
+            if self.dist is None:
+                self.dist = "darwin"
+
+    def __str__(self):
+        return (
+            "Platform:{0}\nMachine:{1}\nVersion:{2}\nExtensions:{3}\nDist:{4}\n".format(
+                self.platform, self.machine, self.version, self.extensions, self.dist
+            )
+        )
+
+    if sys.version_info[0] == 2:
+
+        def __nonzero__(self):
+            return self.bool_overload()
+
+    elif sys.version_info[0] == 3:
+
+        def __bool__(self):
+            return self.bool_overload()
+
+    def bool_overload(self):
+        if self.platform not in SUPPORTED_PLATFORM_MACHINE:
+            reraise(Exception("Unsupported platform: {0}".format(self.platform)))
+        if self.machine not in SUPPORTED_PLATFORM_MACHINE[self.platform]:
+            reraise(Exception("Unsupported machine: {0}".format(self.machine)))
+        if self.extensions is not None and len(self.extensions) > 0:
+            if not (
+                set(self.extensions)
+                <= set(SUPPORTED_EXTENSIONS[self.platform + self.machine])
+            ):
+                reraise(
+                    Exception(
+                        "Extensions not supported: {0}. Supported extensions: {1}".format(
+                            self.extensions,
+                            SUPPORTED_EXTENSIONS[self.platform + self.machine],
+                        )
+                    )
+                )
+        if (
+            self.version.compare(
+                version2=SUPPORTED_MIN_VERSION[self.platform + self.machine].version
+            )
+            < 0
+        ):
+            reraise(
+                Exception(
+                    "Version not supported. Min Version: {0}".format(
+                        SUPPORTED_MIN_VERSION[self.platform + self.machine].version
+                    )
+                )
+            )
+
+        if not get_remote_version_availability(
+            "WasmEdge/WasmEdge", self.version.version
+        ):
+            reraise(
+                Exception(
+                    "Version {0} does not exist in remote repository of WasmEdge".format(
+                        self.version.version
+                    )
+                )
+            )
+        return True
+
+    def prefix(self):
+        return self.platform + self.machine
+
+
+def main(args):
+    global CONST_env_path, CONST_release_pkg, CONST_ipkg, CONST_shell_config, CONST_shell_profile, CONST_lib_dir
+
+    compat = Compat(
+        version=args.version,
+        extensions=args.extensions,
+        platform_=args.platform,
+        machine=args.machine,
+        dist_=args.dist,
+    )
+
+    logging.debug("Compat object: %s", compat)
+    logging.debug("Temp path: %s", TEMP_PATH)
+    logging.debug("CLI Args:")
+    logging.debug(args)
+
+    if len(args.plugins) >= 1:
+        logging.warning("Experimental Option Selected: plugins")
+        logging.warning("plugins option may change later")
+
+    if compat:
+        print("Compatible with current configuration")
+
+        set_consts(args, compat)
+
+        # Run uninstaller
+        uninstaller_path = join(TEMP_PATH, "uninstall.sh")
+        download_url(CONST_urls[WASMEDGE_UNINSTALLER], uninstaller_path)
+
+        print("Running Uninstaller")
+
+        logging.debug(
+            run_shell_command("bash {0}  -p {1} -q".format(uninstaller_path, args.path))
+        )
+        remove(uninstaller_path)
+
+        # If args.path is default then remove it initially
+        if PATH in args.path and exists(args.path):
+            shutil.rmtree(args.path)
+
+        set_env(args, compat)
+
+        logging.debug("CONST_env_path: %s", CONST_env_path)
+        logging.debug("CONST_release_pkg: %s", CONST_release_pkg)
+        logging.debug("CONST_ipkg: %s", CONST_ipkg)
+        logging.debug("CONST_lib_ext: %s", CONST_lib_ext)
+        logging.debug("CONST_urls: %s", CONST_urls)
+        logging.debug("CONST_lib_dir: %s", CONST_lib_dir)
+
+        if getenv("SHELL") != SHELL:
+            logging.warning("SHELL variable not found. Using %s as SHELL", SHELL)
+
+        if shell_configure(args, compat) != 0:
+            logging.error("Error in configuring shell")
+
+        logging.debug("CONST_shell_profile: %s", CONST_shell_profile)
+        logging.debug("CONST_shell_config: %s", CONST_shell_config)
+
+        print("Downloading WasmEdge")
+
+        # Download WasmEdge
+        download_url(
+            CONST_urls[WASMEDGE], join(TEMP_PATH, CONST_release_pkg), show_progress
+        )
+
+        # Extract archive
+        extract_archive(
+            join(TEMP_PATH, CONST_release_pkg),
+            args.path,
+            join(TEMP_PATH),
+            env_file_path=CONST_env_path,
+            remove_finished=True,
+        )
+
+        print("Installing WasmEdge")
+        # Copy the tree
+        for sub_dir in listdir(join(TEMP_PATH, CONST_ipkg)):
+            if sub_dir == "lib64":
+                copytree(join(TEMP_PATH, CONST_ipkg, sub_dir), join(args.path, "lib"))
+            else:
+                copytree(join(TEMP_PATH, CONST_ipkg, sub_dir), join(args.path, sub_dir))
+
+        if is_default_path(args):
+            # perform actions if default path
+            for dir in listdir(args.path):
+                path = join(args.path, dir)
+                if not isdir(path):
+                    continue
+                for subdir in listdir(path):
+                    sub_folder = join(path, subdir)
+                    if isdir(sub_folder):
+                        if any("Plugin" in s for s in listdir(sub_folder)):
+                            # Handle plugins
+                            copytree(sub_folder, join(args.path, "plugin"), True)
+                            shutil.rmtree(sub_folder)
+
+        # Check if wasmedge binary works
+        wasmedge_output = run_shell_command(
+            ". {0}/env && {0}/bin/wasmedge --version".format(args.path)
+        )
+
+        if args.version in wasmedge_output:
+            print("WasmEdge Successfully installed")
+        else:
+            logging.critical(
+                "WasmEdge installation incorrect: {0}".format(wasmedge_output)
+            )
+
+        if IMAGE in args.extensions or "all" in args.extensions:
+            if install_image_extension(args, compat) != 0:
+                logging.error("Error in installing image extensions")
+            else:
+                print("Image extension installed")
+
+        if TENSORFLOW in args.extensions or "all" in args.extensions:
+            if install_tensorflow_extension(args, compat) != 0:
+                logging.error("Error in installing tensorflow extensions")
+            else:
+                print("Tensorflow extension installed")
+
+        install_plugins(args, compat)
+
+        ldconfig(args, compat)
+
+        # Cleanup
+        shutil.rmtree(TEMP_PATH)
+
+        if compat.platform != "Darwin":
+            print("Run:\nsource {0}".format(CONST_shell_config))
+        else:
+            print("Run:\nsource {0}".format(CONST_shell_profile))
+    else:
+        reraise(Exception("Incompatible with your machine\n{0}".format(compat)))
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(
+        description="WasmEdge installation, uninstallation and extensions install"
+    )
+    parser.add_argument(
+        "-e",
+        "--extension",
+        dest="extensions",
+        choices=EXTENSIONS.append("all"),
+        required=False,
+        default=[],
+        nargs="*",
+        help="Supported Extensions - {0}".format(EXTENSIONS),
+    )
+    parser.add_argument(
+        "-v",
+        "--version",
+        dest="version",
+        default=get_latest_github_release("WasmEdge/WasmEdge"),
+        required=False,
+        help="Version for WasmEdge",
+    )
+    parser.add_argument(
+        "-D",
+        "--debug",
+        dest="loglevel",
+        required=False,
+        action="store_const",
+        const=logging.DEBUG,
+        help="Verbosity debug",
+    )
+    parser.add_argument(
+        "-p",
+        "--path",
+        dest="path",
+        required=False,
+        default=PATH,
+        help="Installation path for WasmEdge",
+    )
+    parser.add_argument(
+        "-r",
+        "--remove-old",
+        dest="remove_old",
+        required=False,
+        choices=["yes", "no"],
+        help="Run uninstaller script before installing",
+    )
+    parser.add_argument(
+        "-u",
+        "--uninstall-script-tag",
+        dest="uninstall_script_tag",
+        required=False,
+        default=get_latest_github_release("WasmEdge/WasmEdge"),
+        help="GitHub tag for uninstall script",
+    )
+    parser.add_argument(
+        "--plugins",
+        dest="plugins",
+        required=False,
+        default=[],
+        nargs="*",
+        help="(experimental option)Supported Plugins. Example\n"
+        + "--plugins wasi_crypto:0.11.0\n"
+        + "--plugins wasi_crypto",
+    )
+    parser.add_argument(
+        "--tf-version",
+        dest="tf_version",
+        required=False,
+        default=None,
+        help="Tensorflow and tensorflow lite version",
+    )
+    parser.add_argument(
+        "--tf-deps-version",
+        dest="tf_deps_version",
+        required=False,
+        default=None,
+        help="Tensorflow and tensorflow lite deps version",
+    )
+    parser.add_argument(
+        "--tf-tools-version",
+        dest="tf_tools_version",
+        required=False,
+        default=None,
+        help="Tensorflow and tensorflow lite tools version",
+    )
+    parser.add_argument(
+        "--image-version",
+        dest="image_version",
+        required=False,
+        default=None,
+        help="Image extension version",
+    )
+    parser.add_argument(
+        "--platform",
+        "--os",
+        dest="platform",
+        required=False,
+        default=platform.system(),
+        choices=["Linux", "Darwin"],
+        type=lambda s: s.title(),
+        help="Platform ex- Linux, Darwin, Windows",
+    )
+    parser.add_argument(
+        "--machine",
+        "--arch",
+        dest="machine",
+        required=False,
+        default=platform.machine(),
+        choices=["x86_64", "aarch64", "arm", "arm64"],
+        type=lambda s: s.lower(),
+        help="Machine ex- x86_64, aarch64",
+    )
+    parser.add_argument(
+        "--dist",
+        dest="dist",
+        required=False,
+        default=None,
+        choices=["ubuntu20.04", "manylinux2014"],
+        type=lambda s: s.lower(),
+        help="Dist ex- ubuntu20.04,manylinux2014",
+    )
+    args = parser.parse_args()
+
+    logging.basicConfig(format="%(levelname)-8s- %(message)s", level=args.loglevel)
+
+    args.path = abspath(args.path)
+
+    if args.tf_version is None:
+        args.tf_version = args.version
+
+    if args.tf_deps_version is None:
+        args.tf_deps_version = args.version
+
+    if args.tf_tools_version is None:
+        args.tf_tools_version = args.version
+
+    if args.image_version is None:
+        args.image_version = args.version
+
+    logging.debug("Python Version: %s", sys.version_info)
+    main(args)